<template>
	<movable-area class="mova">
		<view class="ward-container" :style="{
			backgroundColor:showPlainBackground ? '#FFFFE8':'transparent',
			backgroundImage:showPlainBackground?'none':`url(${bgImage})`
			}" @click="handleContainerClick">
			<timeModule class="time-module"></timeModule>
			<bulletScreen></bulletScreen>
			<!-- 坐位未脱衣 -->
			<view v-show="showSeat" class="seat">
				<img :src="characterImages.sittingWithClothes" class="seat-up-img" />
			</view>
			<!-- 坐位暴露上半身 -->
			<view v-show="showSeatUp" class="seat-up">
				<img :src="characterImages.sittingExposed" class="seat-up-img" />
			</view>
			<!-- <image class="seat-up"></image> -->
			<!-- 手腕 -->
			<view class="wrist" @click="handleWristClick"></view>

			<!-- 语音按钮 -->
			<view class="voice" @click="handleVoiceButtonClick" :style="{opacity : activeId === 1 ? '1' : '0',
  visibility: activeId === 1 ? 'visible' : 'hidden' ,
  transition: 'opacity 0.5s ease, visibility 0.5s ease'}">
				<uni-icons class="voice-icon" :type="isRecording ? 'mic-filled' : 'mic'" color="#2A82E4"
					size="45"></uni-icons>
			</view>
			<!-- 语音问诊结果显示区域 -->
			<view class="measurement-bubble"
				v-if="activeId === 1 && voiceResult&&!showVisualDiagnosis&&!showChestExpansion&&!showWholeLung&&!showPleuraExpansion&&!showLungMobility"
				@click.stop style="right: auto; left: 180rpx; top: 50rpx;">
				<view class="bubble-content">
					<view class="bubble-title">问诊记录</view>
					<view class="bubble-text">{{voiceResult}}</view>
				</view>
				<view class="bubble-arrow"></view>
			</view>

			<!-- 视诊内容显示区域 -->
			<view class="visual-diagnosis-container" v-if="showVisualDiagnosis&&activeId===1">
				<view class="introduction-popup" v-if="showIntroductionPopup" @click="hideIntroductionPopup">
					<view class="introduction-content">
						{{currentIntroduction}}
					</view>
				</view>
				<image class="visual-image-next" :src="visualDiagnosisImages.thorax" mode="aspectFit"
					v-if="allViewsCompleted"></image>
				<view class="diagnosis-dialog dialog-next" v-if="allViewsCompleted">
					<text>请汇报当前结果</text>
				</view>
				<view class="diagnosis-dialog dialog-next1" v-if="allViewsCompleted">
					<text>{{ voiceResult || "点击麦克风按钮开始语音识别" }}</text>
				</view>
				<view v-if="showDynamicImage" class="dynamic-image-container">
					<image class="dynamic-image" :src="getCurrentDynamicImage()" mode="aspectFit"></image>
				</view>
				<view v-else>
					<image class="visual-image" :src="getCurrentImage()" mode="aspectFit" v-if="!allViewsCompleted">
					</image>
					<view v-for="(area, areaIndex) in getClickableAreas()" :key="'clickable-'+areaIndex"
						v-if="!allViewsCompleted">
						<view v-for="(rect, rectIndex) in area.rects" :key="'clickable-rect-'+rectIndex"
							class="rect-box clickable-rect" :style="{
				         left: rect.left,
				         top: rect.top,
				         width: rect.width + 'px',
				         height: rect.height + 'px',
						 border:'1px solid ' + rect.color
				       }" @click="handleRectClick(area.id, rectIndex)">
						</view>
					</view>

					<view v-for="(area, areaIndex) in getDrawingAreas()" :key="'drawing-'+areaIndex"
						v-if="!allViewsCompleted">
						<view v-for="(rect, rectIndex) in area.rects" :key="'drawing-rect-'+rectIndex"
							class="rect-box no-click-rect" :style="{
				       	              left: rect.left,
				       	              top: rect.top,
				       	              width: rect.width + 'px',
				       	              height: rect.height + 'px',
									  border:'1px solid ' + rect.color
				       	            }" @click="handleNoClickRect(area.id, rectIndex)" :data-area-id="area.id"
							:data-rect-index="rectIndex">
						</view>
					</view>
				</view>
				<view v-if="showDrawingMode" class="drawing-controls">
					<button @click="clearAllLines" class="drawing-button clear-btn">
						清除所有线条
					</button>
				</view>

				<canvas class="drawing-canvas" canvas-id="drawingCanvas" @touchstart="handleTouchStart"
					@touchmove="handleTouchMove" @touchend="handleTouchEnd" @mousedown="handleMouseDown"
					@mousemove="handleMouseMove" @mouseup="handleMouseUp" v-if="!allViewsCompleted"></canvas>

				<button v-if="showStartDrawingButton" @click="startDrawing" class="drawing-button start-btn">
					开始画线标注
				</button>
				<view class="rect-hint" v-if="showRectHint">
					{{rectHintText}}
				</view>

				<view class="diagnosis-dialog dialog-top ribs-grid" v-if="!allViewsCompleted">
					<text>请指出并说出以下位置</text>
					<view v-for="(item, index) in visualDiagnosisRibs[currentViewIndex]?.areas || []"
						:key="'text-'+index" class="ribs-item">
						{{item.text }}
					</view>
				</view>

				<view class="diagnosis-dialog dialog-bottom" v-if="!allViewsCompleted">
					<view class="dialog-content">
						{{ voiceResult || "请先说出要点击的位置" }}
					</view>
				</view>
				<view class="task-table" v-if="!allViewsCompleted">
					<view class="table-header">
						<view class="header-cell">任务</view>
						<view class="header-cell">结果</view>
					</view>
					<view class="table-body">
						<view class="table-row" v-for="(item,index) in visualDiagnosisRibs[currentViewIndex]?.areas"
							:key="index">
							<view class="body-cell">{{item.text}}</view>
							<view class="body-cell"
								:class="{'completed':isTaskCompleted(index),'error':!isTaskCompleted(index)}">
								{{isTaskCompleted(index)?'完成':'未完成'}}
							</view>
						</view>
					</view>
				</view>
			</view>
			<!-- 胸廓扩张度专用容器 -->
			<view class="chest-expansion-container" v-if="showChestExpansion&&activeId===1">
				<view class="diagnosis-dialog dialog-chest">
					<view class="dialog-content">
						{{ voiceResult || "点击麦克风按钮开始语音识别" }}
					</view>
				</view>

				<!-- 静态图或呼吸动图 -->
				<image class="chest-expansion-image"
					:src="showBreathingAnimation ? getCurrentBreathingImage() : thoraxImage[currentThoraxIndex].url"
					mode="aspectFit" @click="handleImageClick">
				</image>
				<image class="warm-hands-animation" :src="hands.warming" mode="aspectFit" v-if="showWarmHandsAnimation">
				</image>
				<image class="chest-expansion-hands" :src="hands.palpation" mode="aspectFit"
					v-show="chestGame.isCompleted && !showBreathingAnimation">
				</image>

				<!-- 覆盖整个图片的透明点击层 -->
				<view class="chest-click-layer" @click="handleChestClickAnywhere" v-if="!showBreathingAnimation">
				</view>

				<view v-for="(rect, index) in getCurrentRects()" :key="'rect-'+index" class="chest-rect" :class="{
              'target-rect': rect.isTarget,
          }" :style="{
              left: rect.left,
              top: rect.top,
              width: rect.width + 'px',
              height: rect.height + 'px',
			  border:'1px solid ' + rect.color
          }" @click.stop="handleChestClick(rect, rect.areaId, rect.rectIndex)" v-if="!showBreathingAnimation">
				</view>

				<!-- 简单切换按钮 -->
				<button @click="switchView" class="view-switch-btn" v-show="!showReportResult">
					{{ currentThoraxIndex === 0 ? '切换至背面' : '切换至正面' }}
				</button>
				<view class="chest-expansion" v-show="!showReportResult">
					<text>{{ currentThoraxIndex === 0 ? '请选择拇指位置' : '请选择双手手掌放置的位置' }}</text>
				</view>
				<view class="report-prompt" v-if="showReportResult">
					请你汇报结果
				</view>
			</view>
			<!-- 胸膜摩擦感专用容器 -->
			<view class="pleura-expansion-container" v-if="showPleuraExpansion&&activeId===1">
				<view class="diagnosis-dialog dialog-pleura" v-show="!pleuraCompleted">
					<view class="dialog-content">
						{{ voiceResult || "点击麦克风按钮开始语音识别" }}
					</view>
				</view>

				<movable-area class="movable-area">
					<!-- 静态图或呼吸动图 -->
					<image class="pleura-expansion-image" :src="thoraxImages.front" mode="aspectFit"
						v-show="!showBreathingPleAnimation">
					</image>
					<image class="breathing-animation-image" :src="visualDiagnosisImages.frontVideo" mode="aspectFit"
						v-show="showBreathingPleAnimation"></image>
					<view v-for="(rect, index) in pleuraTargetRects" key="'target-'+index" class="pleura-target-rect"
						:style="{
        left: rect.left,
        top: rect.top,
        width: rect.width + 'px',
        height: rect.height + 'px',
        border:'1px solid ' + rect.color
      }">
					</view>

					<!-- 可拖动的左右手 - 使用x和y属性而非style -->
					<movable-view class="hand-draggable left-hand" direction="all" @change="onHandDrag($event, 'left')"
						:x="leftHandPosition.x" :y="leftHandPosition.y">
						<image :src="hands.pleuraLeft" mode="aspectFit"></image>
					</movable-view>

					<movable-view class="hand-draggable right-hand" direction="all"
						@change="onHandDrag($event, 'right')" :x="rightHandPosition.x" :y="rightHandPosition.y">
						<image :src="hands.pleuraRight" mode="aspectFit"></image>
					</movable-view>
				</movable-area>

				<!-- 其他元素保持不变 -->
				<view class="pleura-instruction" v-show="!pleuraCompleted&&!handsPlacedCorrectly">
					<text>请将双手放置在正确位置上</text>
				</view>
				<view class="report-prompt" v-if="pleuraCompleted">
					<text>结果展示：</text><br>
					<text>未触及胸膜摩擦感</text>
				</view>
			</view>
			<!-- 触觉语颤专用容器 -->
			<view class="tactile-fremitus-container" v-if="showTactileFremitus&&activeId ===1">
				<view class="diagnosis-dialog dialog-tactile">
					<view class="dialog-content">
						{{voiceResult||"点击麦克风按钮开始语音识别"}}
					</view>
				</view>
				<image class="tactile-image" :src="getCurrentTactileImage()" mode="aspectFit"></image>
				<movable-area class="movable-area-tactile">
					<movable-view class="hand-draggable left-hand-tactile" direction="all"
						@change="onTactileHandDrag($event,'left')" :x="leftTactileHandPosition.x"
						:y="leftTactileHandPosition.y">
						<image :src="hands.tactileLeft" mode="aspectFit"></image>
					</movable-view>
					<movable-view class="hand-draggable right-hand-tactile" direction="all"
						@change="onTactileHandDrag($event,'right')" :x="rightTactileHandPosition.x"
						:y="rightTactileHandPosition.y">
						<image :src="hands.tactileRight" mode="aspectFit"></image>
					</movable-view>
				</movable-area>
				<view v-for="(position, index) in getCurrentTargetPositions()" :key="'target-'+index"
					class="tactile-target-rect" :style="{
				      left: position.left,
				      top: position.top,
				      width: position.width + 'px',
				      height: position.height + 'px',
					  border:'1px solid ' + position.color
				    }">
					<text class="position-id">{{position.group}}</text>
				</view>
				<button @click="switchTactieView" class="view-switch-btn-tactile"
					v-show="!tactileCompleted&&currentTactileGroup===0">
					{{currentTactileViewIndex===0?'切换至背面':'切换至正面'}}</button>
				<view class="tactile-instruction" v-if="!tactileCompleted">
					<text>{{getTactileInstruction()}}</text>
				</view>
			</view>
			<!-- 全肺叩诊专用容器 -->
			<view class="Whole-lung-container" v-if="showWholeLung">
				<view class="percussion-image-container" v-if="showPercussionImages">
					<text class="percussion-title">请选择正确的叩诊手法</text>
					<view class="percussion-images">
						<image v-for="(img, index) in percussionImages" :key="index" :src="img.url" mode="aspectFit"
							:class="{'correct-image': index === 2}" @click="handlePercussionImageClick(index)"></image>
					</view>
				</view>
				<view class="Whole-lung-image" v-if="showPercussionResult">
					<view class="lung-prompt1" v-show="currentPercussionView===VIEW_TYPES.FRONT">
						<view>
							第二肋间——
						</view><br>
						<view>
							第三肋间——
						</view><br>
						<view>
							第四肋间——
						</view><br>
						<view>
							第五肋间——
						</view><br>
						<view>
							第六肋间——
						</view><br>
						<view>
							第七肋间——
						</view>
					</view>
					<view class="lung-prompt2" v-show="currentPercussionView===VIEW_TYPES.SIDE">
						<view class="yewo">
							腋窝——————
						</view><br>
						<view class="four">
							第四肋间——————
						</view><br>
						<view class="six">
							第六肋间——————
						</view><br>
						<view class="eight">
							第八肋间——————
						</view>
					</view>
					<view class="lung-prompt3" v-show="currentPercussionView===VIEW_TYPES.BACK">
						<view class="two">
							第二肋间隙——————
						</view><br>
						<view class="four">
							第四肋间隙——————
						</view><br>
						<view class="seven">
							第七肋间隙——————
						</view><br>
						<view class="eight">
							第八肋间隙——————
						</view>
						<view class="ten">
							第十肋间隙——————
						</view>
					</view>
					<image :src="getCurrentPercussionImage()" mode="aspectFit" :style="percussionImageStyle"></image>

					<image v-if="percussionHandVisible" class="percussion-hand"
						:class="{ 'pulse-animation': isPercussionActive }" :src="hands.percussion" mode="aspectFit"
						:style="{
					    position: 'absolute',
					    left: percussionHandPosition.left,
					    top: percussionHandPosition.top,
					    width: '60px',
					    height: '60px',
					    zIndex: 101
					  }">
					</image>
					<!-- 渲染所有叩诊点 -->
					<view v-for="(area, areaIndex) in getCurrentPercussionAreas()" :key="'percussion-area-'+areaIndex">
						<view v-for="(rect, rectIndex) in area.rects" :key="'percussion-rect-'+rectIndex"
							class="percussion-rect" :style="{
					            left: rect.left,
					            top: rect.top,
					            width: rect.width + 'px',
					            height: rect.height + 'px',
					            borderColor: rect.color
					          }" @click="handlePercussionPointClick(area.id, rectIndex,rect.id)">
							<text class="percussion-id">{{rect.id}}</text>
						</view>
					</view>
					<!-- 叩诊提示 -->
					<view class="percussion-hint hint-left" v-if="currentPercussionHint">
						{{currentPercussionHint}}
					</view>
					<view class="percussion-hint hint-right">
						{{ getCurrentPercussionHint }}
					</view>
				</view>
			</view>
			<!-- 肺下界叩诊容器 -->
			<view class="lung-lower-bound-container" v-if="showLungLowerBound">
				<view class="lung-lower-bound-image">
					<!-- 提示文本 -->
					<view class="lung-prompt1" v-show="currentPercussionView===VIEW_TYPES.FRONT">
						<view>第二肋间——</view><br>
						<view>第三肋间——</view><br>
						<view>第四肋间——</view><br>
						<view>第五肋间——</view><br>
						<view>第六肋间——</view><br>
						<view>第七肋间——</view>
					</view>
					<view class="lung-prompt2" v-show="currentPercussionView===VIEW_TYPES.SIDE">
						<view class="yewo">腋窝——————</view><br>
						<view class="four">第四肋间隙————</view><br>
						<view class="six">第六肋间隙————</view><br>
						<view class="eight">第八肋间隙————</view><br>
					</view>
					<view class="lung-prompt3" v-show="currentPercussionView===VIEW_TYPES.BACK">
						<view class="two">第二肋间隙————</view><br>
						<view class="seven">第七肋间隙————</view><br>
						<view class="eight">第八肋间隙————</view><br>
						<view class="ten">第十肋间隙————</view><br>
					</view>

					<!-- 叩诊图片 -->
					<image :src="getCurrentLowerBoundImage()" mode="aspectFit" :style="percussionImageStyle"></image>

					<!-- 叩诊手部动画 -->
					<image v-if="percussionHandVisible" class="percussion-hand"
						:class="{ 'pulse-animation': isPercussionActive }" :src="hands.percussion" mode="aspectFit"
						:style="{
          position: 'absolute',
          left: percussionHandPosition.left,
          top: percussionHandPosition.top,
          width: '60px',
          height: '60px',
          zIndex: 101
        }">
					</image>

					<!-- 渲染所有叩诊点 -->
					<view v-for="(area, areaIndex) in getCurrentLowerBoundAreas()" :key="'lower-bound-area-'+areaIndex">
						<view v-for="(rect, rectIndex) in area.rects" :key="'lower-bound-rect-'+rectIndex"
							class="percussion-rect" :style="{
            left: rect.left,
            top: rect.top,
            width: rect.width + 'px',
            height: rect.height + 'px',
            borderColor: rect.color
          }" @click="handleLowerBoundPointClick(area.id, rectIndex, rect.id)">
							<text class="percussion-id">{{rect.id}}</text>
						</view>
					</view>

					<!-- 叩诊提示 -->
					<view class="percussion-hint hint-left" v-if="currentPercussionHint">
						{{currentPercussionHint}}
					</view>
					<view class="percussion-hint hint-right">
						{{ getCurrentLowerBoundHint }}
					</view>
				</view>
			</view>
			<!-- 体温测量倒计时 -->
			<view class="measurement-countdown" v-if="showMeasurementCountdown" :style="{
				opacity: showMeasurementCountdown ? '1' : '0',
				visibility: showMeasurementCountdown ? 'visible' : 'hidden',
				transition: 'opacity 0.5s ease, visibility 0.5s ease'
			}">
				<uni-countdown color="#fff" splitorColor="#fff" :second="measurementCountdown"></uni-countdown>
			</view>
			<!-- 肺下界叩诊填空题弹窗 -->
			<view class="lung-quiz-dialog" v-if="showLungQuizDialog">
				<view class="lung-quiz-content">
					<view class="lung-quiz-title">肺下界叩诊结果</view>
					<view class="lung-quiz-question">
						<text>该患者肺下界分别位于：</text>
						<view class="quiz-input-group">
							<text>锁骨中线第</text>
							<input v-model="quizAnswers.clavicular" class="lung-quiz-input" type="number"
								placeholder="?" />
							<text>肋间隙、</text>
						</view>
						<view class="quiz-input-group">
							<text>腋中线第</text>
							<input v-model="quizAnswers.axillary" class="lung-quiz-input" type="number"
								placeholder="?" />
							<text>肋间隙和</text>
						</view>
						<view class="quiz-input-group">
							<text>肩胛线的第</text>
							<input v-model="quizAnswers.scapular" class="lung-quiz-input" type="number"
								placeholder="?" />
							<text>肋间隙</text>
						</view>
					</view>

					<button class="lung-quiz-submit" @click="submitLungQuiz">提交</button>
				</view>
			</view>
			<!-- 肺下界移动度 -->
			<view class="lung-mobility-container" v-if="showLungMobility">
				<view class="lung-mobility-image">
					<view class="lung-prompt3">
						<view class="two">第二肋间隙————</view><br>
						<view class="seven">第七肋间隙————</view><br>
						<view class="eight">第八肋间隙————</view><br>
						<view class="ten">第十肋间隙————</view><br>
					</view>
					<image :src="visualDiagnosisImages.wholeLungBack" mode="aspectFit"></image>
					<image v-if="percussionHandVisible" class="percussion-hand"
						:class="{'pulse-animation':isPercussionActive}" :src="hands.percussion" mode="aspectFit" :style="{
					          position: 'absolute',
					          left: percussionHandPosition.left,
					          top: percussionHandPosition.top,
					          width: '60px',
					          height: '60px',
					          zIndex: 101
					        }"></image>
					<view v-for="(area,areaIndex) in getCurrentMobilityAreas()" :key="'mobility-area-'+areaIndex">
						<view v-for="(rect,rectIndex) in area.rects" :key="'mobility-rect-'+rectIndex"
							class="percussion-rect"
							:class="{'marked-rect':isRectMarked(rect.id),'marking-mode':isMarkingMode}" :style="{
							left:rect.left,
							top:rect.top,
							width:rect.width+'px',
							height:rect.height+'px',
							zIndex:105,
							borderColor: isRectMarked(rect.id) ? 'green' : rect.color
						}" @click="isMarkingMode?markTargetPoint(rect.id):handleMobilityPointClick(area.id,rectIndex,rect.id)">
							<!-- <text class="percussion-id">{{rect.id}}</text> -->
						</view>
					</view>
					<view v-for="(rect, index) in getAllMarkedPointsFromAllPhases()" :key="'persistent-marked-'+index"
						class="percussion-rect marked-rect" :style="{
					    left: rect.left,
					    top: rect.top,
					    width: rect.width+'px',
					    height: rect.height+'px',
					    borderColor: 'green',
					    zIndex: 102 // 确保在其他元素上方
					  }">
						<view class="center-dot"></view>
					</view>
					<!-- 优化后的尺子 -->
					<view class="ruler-container" v-if="lungMobilityState.markedPoints.length === 3" :style="{
				                left: `${parseInt(markedPointsLeft) + 25}px`,
				                top: markedPointsTop,
				                height: markedPointsHeight
				            }">
						<view class="ruler-line" :style="{height: markedPointsHeight}"></view>

						<view class="ruler">
							{{ markedPointsDistance }}cm
						</view>

						<!-- 刻度标记 -->
						<view v-for="(mark, idx) in rulerMarks" :key="idx" class="ruler-mark" :style="{
				                    top: `${idx * (parseInt(markedPointsHeight)/4)}px`
				                }">
							<text class="ruler-mark-label">{{ mark }}cm</text>
						</view>
					</view>
				</view>
				<view class="percussion-hint hint-mobility1">
					{{getCurrentMobilityHint}}
				</view>
				<view class="diagnosis-dialog hint-mobility2" v-if="isShowRecording">
					<view class="dialog-content">
						{{ voiceResult || "点击麦克风按钮开始语音识别" }}
					</view>
				</view>
			</view>
			<!-- 全肺听诊容器 -->
			<view class="Whole-lung-container" v-if="showWholeLungAuscultation">
				<view class="Whole-lung-image">
					<view class="lung-prompt1" v-show="currentPercussionView===VIEW_TYPES.FRONT">
						<view>
							第二肋间——
						</view><br>
						<view>
							第三肋间——
						</view><br>
						<view>
							第四肋间——
						</view><br>
						<view>
							第五肋间——
						</view><br>
						<view>
							第六肋间——
						</view><br>
					</view>
					<image :src="getCurrentPercussionImage()" mode="aspectFit" :style="percussionImageStyle"></image>

					<image v-if="percussionHandVisible" class="percussion-hand" :src="visualDiagnosisImages.stethoscope"
						mode="aspectFit" :style="{
					    position: 'absolute',
					    left: percussionHandPosition.left,
					    top: percussionHandPosition.top,
					    width: '60px',
					    height: '60px',
					    zIndex: 101
					  }">
					</image>
					<!-- 渲染所有叩诊点 -->
					<view v-for="(area, areaIndex) in getCurrentAuscultationAreas()"
						:key="'percussion-area-'+areaIndex">
						<view v-for="(rect, rectIndex) in area.rects" :key="'percussion-rect-'+rectIndex"
							class="percussion-rect" :style="{
					            left: rect.left,
					            top: rect.top,
					            width: rect.width + 'px',
					            height: rect.height + 'px',
					            borderColor: rect.color
					          }" @click="handleAuscultationPointClick(area.id, rectIndex,rect.id)">
							<text class="percussion-id">{{rect.id}}</text>
						</view>
					</view>
					<!-- 听诊提示 -->
					<view class="percussion-hint hint-left" v-if="currentPercussionHint">
						{{currentPercussionHint}}
					</view>
					<view class="percussion-hint hint-right">
						{{ getCurrentAuscultationHint }}
					</view>
				</view>
			</view>
			<!-- 可拖动物品块和目标位置，统一用 template v-for 包裹，确保 item 作用域 -->
			<template v-for="item in visibleItems" :key="item.id">
				<movable-view class="thermometer" :inertia="false" :animation="false" :x="item.x" :y="item.y"
					:disabled="item.isLocked" @change="e => onDrag(e, item)" @touchend="e => onDragEnd(e, item)"
					@touchcancel="e => onDragEnd(e, item)"
					v-if="item && (item.isLocked || (overlayVisible && item.visible))" direction="all" damping="80"
					out-of-bounds="false">
					<image class="thermometer" :src="item.image" mode="aspectFit"></image>
					<view class="drag-hint" v-if="!item.isLocked">拖动至指定位置</view>
				</movable-view>
				<view class="target-location" v-if="item && (item.isLocked || overlayVisible)"
					:class="{'target-active': false, 'target-complete': item.isLocked}"></view>
			</template>

			<!-- 测量结果气泡 -->
			<view class="measurement-bubble" v-if="showMeasurementResult" @click.stop
				:class="{'bubble-hide': bubbleHiding}"
				:style="{animation: bubbleHiding ? 'bubble-hide 0.3s forwards' : 'bubble-appear 0.5s ease'}">
				<view class="bubble-content">
					<view class="bubble-text">{{measurementResult}}</view>
				</view>
				<view class="bubble-arrow"></view>
			</view>

			<!-- 医疗车页面按钮 -->
			<view class="overlay" :style="{
        backgroundImage: `url(${uiImages.treatmentCartIcon})`,
        display: (shouldHideCartIcon || showVisualDiagnosis) ? 'none' : 'block'
      }" @click="toggleOverlay"></view>

			<!-- 医疗车放大页面 -->
			<view class="medical-vehicle" :style="{ opacity: overlayVisible ? '1' : '0',
				visibility: overlayVisible ? 'visible' : 'hidden',
				transition: 'opacity 0.5s ease, visibility 0.5s ease',
				backgroundImage:`url(${uiImages.treatmentCartPopupBg})`}">
				<!-- 新增中间层图片 -->
				<image class="medical-middle-img" :src="uiImages.treatmentCartPopupMiddle" mode="aspectFit" />

			</view>

			<!-- 题目页面 -->
			<view class="title-container" v-if="showTitleContainer">
				<view class="title-header">
					<text class="title-text">选择题</text>
					<text class="question-counter">{{currentQuestionIndex + 1}}/{{questions.length}}</text>
				</view>

				<scroll-view scroll-y="true" class="title-scroll-content">
					<view v-if="!questionCompleted" class="question-content">
						<view class="question-text">{{questions[currentQuestionIndex].question}}</view>

						<view class="options-container">
							<view v-for="option in questions[currentQuestionIndex].options" :key="option.id"
								class="option-item" :class="{
									'selected': userAnswer === option.id,
									'correct': showAnswerResult && option.correct,
									'incorrect': showAnswerResult && userAnswer === option.id && !option.correct
								}" @click="selectAnswer(option.id)">
								<text class="option-id">{{option.id}}</text>
								<text class="option-text">{{option.text}}</text>
							</view>
						</view>

						<view v-if="showAnswerResult" class="answer-result">
							<view class="result-icon" :class="isAnswerCorrect ? 'correct' : 'incorrect'">
								<!-- <uni-icons :type="isAnswerCorrect ? 'checkmarkempty' : 'closeempty'" size="20" :color="isAnswerCorrect ? '#4CAF50' : '#F44336'"></uni-icons> -->
							</view>
							<text class="result-text" :class="isAnswerCorrect ? 'correct' : 'incorrect'">
								{{isAnswerCorrect ? '回答正确！' : '回答错误'}}
							</text>
							<button class="next-btn" @click="nextQuestion">
								{{currentQuestionIndex < questions.length - 1 ? '下一题' : '完成'}}
							</button>
						</view>
					</view>

					<view v-else class="completion-content">
						<view class="completion-icon">
							<uni-icons type="checkmarkempty" size="40" color="#4CAF50"></uni-icons>
						</view>
						<text class="completion-text">答题完成！</text>
						<view class="completion-buttons">
							<button class="restart-btn" @click="resetAllQuestions">重新开始</button>
							<button class="close-btn" @click="showTitleContainer = false">关闭页面</button>
						</view>
					</view>
				</scroll-view>
			</view>

			<!-- 模糊层 -->
			<scroll-view scroll-y="true" show-scrollbar="false" class="blur-layer" :style="{width: layerWidth}">

				<!-- 滚动按钮 -->
				<view class="button-container">
					<view class="button-item" v-for="item in buttonList" :key="item.id"
						:class="{ 'active': activeId === item.id }" @click.stop="expand(item.id)">
						<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
							:color="activeId === item.id ? '#FFFFFF' : '#2A82E4'"></uni-icons>
						<text v-if="layerWidth !== '100rpx'">{{item.text}}</text>
					</view>
				</view>
				<!-- 左侧内容区域 -->

				<!-- 根据 activeId 显示对应内容 -->
				<view v-if="activeId === 1" class="content-panel"></view>

				<view v-if="activeId === 2" class="content-panel">
					<text>▲生命体征检测</text>
					<view class="block">
						<view class="project-container" v-for="item in lifeList" @click.stop="carryOutProject(item)"
							:key="item.id">
							<view class="project-button">
								<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
									color="#2A82E4"></uni-icons>
							</view>
							<text>{{item.text}}</text>
						</view>
					</view>
				</view>

				<view v-if="activeId === 3" class="content-panel">
					<text>▲选择患者的指定体位</text>
					<scroll-view class="bodyPosition" scroll-y="true" show-scrollbar="false">
						<view class="project-container" v-for="item in positionList" :key="item.id"
							@click="selectPosition(item)">
							<view class="project-button">
								<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
									color="#2A82E4"></uni-icons>
								<text>{{item.text}}</text>
							</view>
							<!-- <text>{{item.text}}</text> -->
						</view>
					</scroll-view>
				</view>

				<view v-if="activeId === 4" class="content-panel">
					<text>▲选择衣服的指定条件</text>

					<scroll-view class="bodyPosition" scroll-y="true" show-scrollbar="false">
						<view class="project-container" v-for="item in clothList" :key="item.id"
							@click="selectPosition(item)">
							<view class="project-button">
								<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
									color="#2A82E4"></uni-icons>
								<text>{{item.text}}</text>
							</view>
							<!-- <text>{{item.text}}</text> -->
						</view>
					</scroll-view>
				</view>

				<view v-if="activeId === 5" class="content-panel">
					<scroll-view scroll-y="true" class="physical">
						<view style="margin: 0 0 5rpx 0;">▲体格检查</view>
						<text>▲视诊</text>
						<view class="block">
							<view class="project-container" v-for="item in visualDiagnosis" :key="item.id"
								@click.stop="handleVisualDiagnosisClick(item)">
								<view class="project-button">
									<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
										color="#2A82E4"></uni-icons>
								</view>
								<text>{{item.text}}</text>
							</view>
						</view>
						<text>▲触诊</text>
						<view class="block">
							<view class="project-container" v-for="item in palpation" :key="item.id"
								@click.stop="handlePalpationCheck(item)">
								<view class="project-button">
									<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
										color="#2A82E4"></uni-icons>
								</view>
								<text>{{item.text}}</text>
							</view>
						</view>
						<text>▲叩诊</text>
						<view class="block">
							<view class="project-container" v-for="item in percussion" :key="item.id"
								@click.stop="handlePercussionCheck(item)">
								<view class="project-button">
									<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
										color="#2A82E4"></uni-icons>
								</view>
								<text>{{item.text}}</text>
							</view>
						</view>
						<text>▲听诊</text>
						<view class="block">
							<view class="project-container" v-for="item in auscultation" :key="item.id"
								@click.stop="handleAuscultationCheck(item)">
								<view class="project-button">
									<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
										color="#2A82E4"></uni-icons>
								</view>
								<text>{{item.text}}</text>
							</view>
						</view>

					</scroll-view>

				</view>

				<view v-if="activeId === 6" class="content-panel">
					<text>▲辅助检查</text>
					<view class="block">
						<view class="project-container" v-for="item in lifeList" :key="item.id">
							<view class="project-button">
								<uni-icons class="icon" custom-prefix="iconfont" :type="item.type" size="40"
									color="#2A82E4"></uni-icons>
							</view>
							<text>{{item.text}}</text>
						</view>
					</view>
				</view>
			</scroll-view>
		</view>
	</movable-area>
	<!-- 体温测量填空弹窗 -->
	<view class="temp-quiz-dialog" v-if="showTempQuizDialog">
		<view class="temp-quiz-content">
			<image :src="tempQuizImage" class="temp-quiz-image" mode="aspectFit"></image>
			<view class="temp-quiz-question">
				<text>患者现在的体温是</text>
				<input v-model="tempQuizUserInput" class="temp-quiz-input" type="number" placeholder="请填写体温" />
				<text>℃</text>
			</view>
			<button class="temp-quiz-submit" @click="submitTempQuiz">提交</button>
			<view v-if="tempQuizResult" class="temp-quiz-result"
				:class="{correct: tempQuizResult === '回答正确', incorrect: tempQuizResult === '回答错误'}">
				{{ tempQuizResult }}
			</view>
			<button v-if="tempQuizResult === '回答正确'" class="temp-quiz-close"
				@click="showTempQuizDialog = false">关闭</button>
		</view>
	</view>
</template>

<script setup>
	import {
		ref,
		watch,
		nextTick,
		onMounted,
		onUnmounted,
		computed,
		reactive
	} from 'vue'
	import {
		onShow
	} from '@dcloudio/uni-app'
	import {
		items as resourceItems,
		bgImages,
		questions as resourceQuestions,
		characterImages,
		uiImages,
		visualDiagnosisImages,
		quizImages,
		thoraxImages,
		hands,
		audioResources
	} from '@/src/dataResource.js';
	import {
		startCountdown
	} from '../../components/timeModule/countdown'

	// 辅助函数：安全执行函数，防止未处理的异常
	const safeExecute = (fn, fallbackValue = undefined) => {
		try {
			return fn()
		} catch (e) {
			if (e && (e.stack || e.message)) {
				console.error('执行函数出错:', e.stack || e.message)
			} else {
				console.error('执行函数出错:', typeof e === 'object' ? JSON.stringify(e) : String(e))
			}
			return fallbackValue
		}
	}
	const showWarmHandsAnimation = ref(false);
	const warmHandsCompleted = ref(false);
	const showPlainBackground = ref(false)
	const noClickRects = ref([]);
	const showChestExpansion = ref(false);
	const showPleuraExpansion = ref(false);
	// 拖动相关变量
	const triggerDistance = ref(40) // 触发距离阈值（增大以适应新的目标位置）
	// const elementX = ref(550) // 初始X位置
	// const elementY = ref(80) // 初始Y坐标
	// const isInTarget = ref(false) // 是否在目标区域内
	// const isLocked = ref(false) // 元素是否已锁定在目标位置
	// const isDragging = ref(false) // 是否正在拖动
	// const isInTargetArea = ref(false) // 是否在目标区域附近
	// const isResetting = ref(false) // 是否正在重置位置
	// const dragStartTime = ref(0) // 开始拖动的时间戳
	// const dragTimeout = ref(null) // 拖动超时计时器

	// 批量管理拖动物品的数据结构
	const dragItems = ref(resourceItems.map((item, idx) => {
		// 针对尺子和记号笔单独设置位置和大小
		if (item.id === 'ruler') {
			return {
				...item,
				type: item.id,
				x: 460, // 你可以根据实际需求调整
				y: 140,
				initialX: 460,
				initialY: 140,
				// width: 80,
				// height: 24,
				targetX: 180,
				targetY: 240,
				visible: true,
				isLocked: false
			}
		} else if (item.id === 'markerPen') {
			return {
				...item,
				type: item.id,
				x: 480, // 你可以根据实际需求调整
				y: 140,
				initialX: 480,
				initialY: 140,
				// width: 32,
				// height: 32,
				targetX: 180,
				targetY: 240,
				visible: true,
				isLocked: false
			}
		} else if (item.id === 'stethoscope') {
			return {
				...item,
				type: item.id,
				x: 480, // 你可以根据实际需求调整
				y: 140,
				initialX: 550,
				initialY: 170,
				// width: 32,
				// height: 32,
				targetX: 180,
				targetY: 240,
				visible: true,
				isLocked: false
			}
		} else {
			// 其他物品自动错开排列
			return {
				...item,
				type: item.id,
				x: 550 + idx * 70,
				y: 120,
				initialX: 550 + idx * 70,
				initialY: 120,
				targetX: 180,
				targetY: 240,
				visible: true,
				isLocked: false
			}
		}
	}));

	// 计算属性：只渲染可见物品
	const visibleItems = computed(() => dragItems.value.filter(item => item && item.visible));

	function updateVisibleItems() {
		dragItems.value.forEach(item => {
			const pos = uni.getStorageSync(item.id + 'Position');
			item.visible = pos === 'ward';
		});
	}

	onMounted(() => {
		updateVisibleItems();
		dragItems.value.forEach(item => {
			console.log(`${item.name}(${item.id}) Position:`, uni.getStorageSync(item.id + 'Position'));
		});
	})
	onMounted(() => {
		// 预加载动态图
		Object.values(dynamicImages).forEach(url => {
			uni.getImageInfo({
				src: url,
				success: () => console.log('图片预加载成功:', url),
			});
		});
	});


	// 测量相关变量
	const showMeasurementResult = ref(false) // 是否显示测量结果
	const measurementResult = ref('') // 测量结果
	const bubbleHiding = ref(false) // 控制气泡淡出动画
	const isMeasuring = ref(false) // 是否正在测量中
	const measurementCountdown = ref(0) // 测量倒计时

	// 新增：测量倒计时显示控制，仅体温计测量时为 true
	const showMeasurementCountdown = ref(false)
	// 新增：测量体温模式标志
	const isTempMeasureMode = ref(false) // 是否主动测温模式
	// 新增：体温计已就位待测量标志
	const isThermometerReady = ref(false) // 体温计已锁定但未测量
	// 新增：医疗车上方提示
	const showCarHint = ref(false)
	// 新增：题目页面显示控制
	const showTitleContainer = ref(false)

	// 题目相关变量
	const currentQuestionIndex = ref(0)
	const userAnswer = ref(null)
	const showAnswerResult = ref(false)
	const isAnswerCorrect = ref(false)
	const questionCompleted = ref(false)

	// 题目数据
	const questions = ref(resourceQuestions);

	// 脉搏呼吸测量相关变量
	const isPulseBreathMeasuring = ref(false) // 是否正在测量脉搏呼吸
	const pulseResult = ref('') // 脉搏结果
	const breathResult = ref('') // 呼吸结果
	const showPulseResult = ref(false) // 是否显示脉搏结果
	const showBreathResult = ref(false) // 是否显示呼吸结果

	// 当前锁定物品id
	const lockedItemId = ref(null)

	// 当前功能按钮类型
	const currentFunctionType = ref(null)

	// 功能按钮与物品类型映射
	const functionTypeMap = {
		1: 'thermometer', // 测量体温
		2: 'bloodPressure', // 测量血压
		// 可扩展
	}

	// 只选分钟的picker相关变量
	// const minuteOptions = Array.from({length: 59}, (_, i) => i + 1)
	// const selectedMinuteIndex = ref(9) // 默认10分钟
	// const selectedMinutes = ref(10)
	// const hasSelectedTime = ref(false)

	// function bindMinuteChange(e) {
	//   selectedMinuteIndex.value = e.detail.value
	//   selectedMinutes.value = minuteOptions[selectedMinuteIndex.value]
	//   hasSelectedTime.value = true
	//   uni.showToast({
	//     title: `已选择${selectedMinutes.value}分钟`,
	//     icon: 'success',
	//     duration: 1500
	//   })
	// }

	// 图片资源
	const dragItemImage = ref('https://i.postimg.cc/TYGVBbzQ/image-png-2.png')
	// 监听并更新数据
	const onDrag = (e, item) => {
		safeExecute(() => {
			if (!item) return;
			// 只更新元素位置，不做锁定
			item.x = e.detail.x
			item.y = e.detail.y
		})
	}

	// 强制回到初始位置（通过直接DOM操作，作为最后的后备方案）
	const forceResetPosition = () => {
		safeExecute(() => {
			if (!dragItems.value || !Array.isArray(dragItems.value)) return;
			// 使用uni的API直接操作DOM
			const query = uni.createSelectorQuery()
			query.select('.thermometer').boundingClientRect(data => {
				if (data) {
					console.log('强制重置元素位置')
					// 重置所有状态
					dragItems.value.forEach(item => {
						item.x = item.initialX
						item.y = item.initialY
						item.isLocked = false
					})
				}
			}).exec()
		})
	}

	// 拖动结束事件
	const onDragEnd = (e, item) => {
		safeExecute(() => {
			if (!item) return;
			if (item.isLocked) return // 如果已锁定则不处理

			// 体温计无需选择时间，直接判断位置
			// 计算与目标位置的距离
			const distance = Math.sqrt(
				Math.pow(item.x - item.targetX, 2) +
				Math.pow(item.y - item.targetY, 2)
			)
			if (distance <= triggerDistance.value) {
				// 元素在目标区域内，锁定在目标位置
				lockElementToTarget(item)
			} else {
				// 元素不在目标区域，立即回到初始位置
				resetElementPosition(item)
				// 强制触发更新视图
				nextTick(() => {
					setTimeout(() => {
						if (Math.abs(item.x - item.initialX) > 5 || Math.abs(item.y - item
								.initialY) > 5) {
							forceResetPosition()
						}
					}, 100)
				})
			}
		})
	}

	// 将元素锁定到目标位置
	const lockElementToTarget = (item) => {
		// 体温计无需选择倒计时，直接锁定
		// 移除提示
		// if (item.type === 'thermometer') {
		//   uni.showToast({
		//     title: '请先选择测量时间',
		//     icon: 'none',
		//     duration: 2000
		//   })
		//   return
		// }
		// 先解锁所有物品
		dragItems.value.forEach(i => {
			i.isLocked = false
		})
		item.x = item.targetX
		item.y = item.targetY
		item.isLocked = true
		lockedItemId.value = item.id
		// 体温计和血压计锁定后，提示点击功能按钮开始测量
		if (item.type === 'thermometer' || item.type === 'bloodPressure') {
			uni.showToast({
				title: '请点击对应功能按钮开始测量',
				icon: 'none',
				duration: 2000
			})
		}
		// 增加自动开始测量逻辑
		if (currentFunctionType.value && functionTypeMap[currentFunctionType.value] === item.type) {
			startMeasurementByType(item.type)
		}
	}

	// 立即重置元素位置
	const resetElementPosition = (item) => {
		safeExecute(() => {
			// 只有未锁定时才重置物品状态
			if (!item.isLocked) {
				item.x = item.initialX
				item.y = item.initialY
				item.isLocked = false
			}
		})
	}

	// 当覆盖层关闭时，重置所有拖动状态
	const resetDragState = () => {
		safeExecute(() => {
			// 如果未锁定，则重置体温计位置
			dragItems.value.forEach(item => {
				if (!item.isLocked) {
					resetElementPosition(item)
				}
				item.isDragging = false
				if (item.dragTimeout) {
					clearTimeout(item.dragTimeout)
					item.dragTimeout = null
				}
			})
			// 重置测量结果状态，但如果正在测量则保留
			if (!dragItems.value.some(item => item.isLocked) && !isMeasuring.value) {
				showMeasurementResult.value = false
				measurementResult.value = ''
			}
		})
	}

	// 切换计时器显示并重置倒计时
	const timerVisible = ref(false)
	const showCountdown = ref(true)

	const toggleTimer = () => {
		// 如果正在测量体温，不允许操作计时器
		if (isMeasuring.value) {
			uni.showToast({
				title: '体温测量中，请稍候...',
				icon: 'none',
				duration: 1500
			})
			return
		}

		// 关闭气泡
		if (showMeasurementResult.value) {
			hideBubble()
		}

		timerVisible.value = !timerVisible.value
		if (timerVisible.value) {
			// 重置倒计时 - 先销毁再重建组件
			showCountdown.value = false
			nextTick(() => {
				showCountdown.value = true
			})
		}
		startCountdown()
	}

	const overlayVisible = ref(false)

	// 切换显示医疗车覆盖页面
	const toggleOverlay = () => {
		overlayVisible.value = !overlayVisible.value;

		// 如果关闭弹窗，恢复图标显示
		if (!overlayVisible.value) {
			shouldHideCartIcon.value = false;
		}
		safeExecute(() => {
			// 如果正在测量中，不允许关闭医疗车覆盖层
			if (isMeasuring.value && isThermometerReady.value && !overlayVisible.value) {
				uni.showToast({
					title: '正在测量体温，请稍候...',
					icon: 'none',
					duration: 1500
				})
				return
			}

			// 关闭气泡
			if (showMeasurementResult.value) {
				hideBubble()
			}

			overlayVisible.value = !overlayVisible.value
			// 只有关闭医疗车时才重置测量体温模式和体温计就位状态
			if (!overlayVisible.value) {
				isTempMeasureMode.value = false;
				isThermometerReady.value = false;
			}
			if (overlayVisible.value) {
				// 显示覆盖层时重置拖动状态，但如果正在测量则不重置
				if (!isMeasuring.value || !isThermometerReady.value) {
					resetDragState()
				}
			}
		})
	}

	const titleList = ref([

	])
	// 退出视诊时恢复显示
	// const exitVisualDiagnosis = () => {
	// 	shouldHideCartIcon.value = false;
	// 	showVisualDiagnosis.value = false;
	// };
	// 右侧按钮列表
	const buttonList = ref([{
			id: 1,
			type: 'icon-wenzhen'
		},
		{
			id: 2,
			type: 'icon-zhenduan'
		},
		{
			id: 3,
			type: 'icon-tiwei'
		},
		{
			id: 4,
			type: 'icon-chuanyi'
		},
		{
			id: 5,
			type: 'icon-zhenduan'
		},
		{
			id: 6,
			type: 'icon-zhenduan'
		},
	])

	// 体位选择数据
	const positionList = ref([{
			id: 1,
			type: 'icon-tiwei',
			text: '卧位'
		},
		{
			id: 2,
			type: 'icon-tiwei',
			text: '坐位'
		},
		{
			id: 3,
			type: 'icon-tiwei',
			text: '半卧位'
		},
		{
			id: 4,
			type: 'icon-tiwei',
			text: '侧卧位'
		},
		{
			id: 5,
			type: 'icon-tiwei',
			text: '俯卧位'
		},
		{
			id: 6,
			type: 'icon-tiwei',
			text: '膝胸卧位'
		},
		{
			id: 7,
			type: 'icon-tiwei',
			text: '截石位'
		},
		{
			id: 8,
			type: 'icon-tiwei',
			text: '头低足高位'
		},
	])

	//穿衣数据
	const clothList = ref([{
			id: 1,
			type: 'icon-tiwei',
			text: '穿衣'
		},
		{
			id: 2,
			type: 'icon-tiwei',
			text: '暴露上半身'
		},
		{
			id: 3,
			type: 'icon-tiwei',
			text: '暴露下半身'
		},
		{
			id: 4,
			type: 'icon-tiwei',
			text: '全身暴露'
		},
	])

	// 生命检测数据
	const lifeList = ref([{
			id: 1,
			type: 'icon-yiliaofuwu',
			text: '测量体温'
		},
		{
			id: 2,
			type: 'icon-yiliaofuwu',
			text: '测量血压'
		},
		// { id: 3, type: 'icon-yiliaofuwu' ,text:'测量脉搏呼吸'},
	])

	// 体格检查数据
	//视诊
	const shouldHideCartIcon = ref(false); // 控制是否隐藏医疗车图标
	const showVisualDiagnosis = ref(false)
	const dialogTexts = ref(['', ''])
	const visualDiagnosis = ref([{
		id: 1,
		type: 'icon-yiliaofuwu',
		text: '胸廓前后径比例'
	}, ])
	// 跟踪每个区域的点击状态：{ 区域id: { 已点击的矩形索引, 首次点击时间 } }
	const areaClickStates = ref({})
	//视诊肋骨数据
	// 定义视图枚举
	const VIEW_TYPES = {
		FRONT: 0, // 正面
		SIDE: 1, // 侧面
		BACK: 2, // 背面
	};
	const visualDiagnosisRibs = ref({
		[VIEW_TYPES.FRONT]: {
			areas: [{
					id: 1,
					text: "锁骨上窝",
					rects: [{
							left: '113px',
							top: '15px',
							width: 20,
							height: 20,
							color: 'red'
						},
						{
							left: '200px',
							top: '15px',
							width: 20,
							height: 20,
							color: 'red'
						}
					],
					introduction: '锁骨上窝整体是在锁骨上方，属于凹陷区，并且左右两侧各有一个。',
					requireAll: true, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 2,
					text: "胸骨上窝",
					rects: [{
						left: '152px',
						top: '19px',
						width: 20,
						height: 20,
						color: 'red'
					}],
					introduction: '胸骨上窝：为胸骨柄上方的凹陷部，正常气管位于其后。',
					requireAll: false, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 3,
					text: "胸骨角",
					rects: [{
						left: '152px',
						top: '60px',
						width: 20,
						height: 20,
						color: 'red'
					}],
					introduction: '胸骨角（Louis角）：由胸骨柄与胸骨体的连接处向前突起而成,其两侧分别与左右第2肋软骨连接,为前胸壁计数肋骨和肋间隙的重要标志。此外,也是气管分叉、心房上缘和上下纵隔交界的重要标志，相当于第4或第5胸椎的水平。',
					requireAll: false, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 4,
					text: "胸骨下角",
					rects: [{
						left: '151px',
						top: '149px',
						width: 20,
						height: 20,
						color: 'red'
					}],
					introduction: '胸骨下角（腹上角）：为前胸下缘左右肋弓(由7〜10）肋软骨相互连接而成，在胸骨下端汇合形成的夹角，相当于横膈的穹窿部。正常约70°〜110°。其后为肝脏左叶、胃及胰腺所在区域。临床上常以此作为判断体型的标志，体型瘦长者角度较锐，矮胖者较钝,深吸气时可稍增宽。',
					requireAll: false, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 5,
					text: "第2肋间隙",
					rects: [{
							left: '208px',
							top: '73px',
							width: 20,
							height: 20,
							color: 'red'
						},
						{
							left: '91px',
							top: '73px',
							width: 20,
							height: 20,
							color: 'red'
						}
					],
					introduction: '第二肋间（隙）：位于第二肋骨和第三肋骨之间的解剖区域，属于胸壁的重要骨性标志。该区域在临床医学中常用于听诊心脏杂音、定位穿刺操作，并与胸骨角（平第二肋）共同作为肋骨计数的关键参考点。',
					requireAll: true, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 6,
					text: "右锁骨左锁骨中线",
					rects: [{
							left: '103px',
							top: '34px',
							width: 22,
							height: 200,
							color: 'blue'
						},
						{
							left: '198px',
							top: '34px',
							width: 22,
							height: 200,
							color: 'blue'
						}
					],
					introduction: '右锁骨中线：通过锁骨中点向下的垂直线，为通过右锁骨的肩峰端与胸骨端两者中点(锁骨中点)向下的垂直线。',
					requireAll: true, // 需要画两条线
					timeout: 5000, // 5秒内完成
					drawingTask: true // 标记为画线任务
				},
				{
					id: 7,
					text: "前正中线",
					rects: [{
						left: '151px',
						top: '6px',
						width: 22,
						height: 229,
						color: 'blue'
					}],
					introduction: '前正中线（胸骨中线）：为胸骨正中的垂直线,以胸骨柄上缘中点为起点,向下通过剑突中央的垂直线。',
					requireAll: false,
					timeout: 3000, // 3秒内完成
					drawingTask: true // 标记为画线任务
				},
			]
		},
		[VIEW_TYPES.SIDE]: {
			areas: [{
					id: 9,
					text: "腋窝",
					rects: [{
						left: '145px',
						top: '73px',
						width: 15,
						height: 15,
						color: 'blue'
					}],
					introduction: '腋窝：为左、右上肢内侧与胸壁相连的凹陷。',
					requireAll: false,
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 10,
					text: "腋前线",
					rects: [{
						left: '136px',
						top: '73px',
						width: 10,
						height: 150,
						color: 'blue'
					}],
					introduction: '腋前线：为通过左、右腋窝前皱嬖沿前侧胸壁向下的垂直。',
					requireAll: false,
					timeout: 3000, // 3秒内完成
					drawingTask: true
				},
				{
					id: 11,
					text: "腋中线",
					rects: [{
						left: '148px',
						top: '73px',
						width: 10,
						height: 150,
						color: 'blue'
					}],
					introduction: '腋中线：自腋窝顶端于腋前线和腋后线之间中点向下的垂线。',
					requireAll: false, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: true
				},
				{
					id: 12,
					text: "腋后线",
					rects: [{
						left: '162px',
						top: '79px',
						width: 10,
						height: 150,
						color: 'blue'
					}],
					introduction: '腋后线：为通过左、右腋窝后皱嬖沿后侧胸壁向下的垂直。',
					requireAll: false, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: true
				}
			]
		},
		[VIEW_TYPES.BACK]: {
			areas: [{
					id: 6,
					text: "肩胛下角",
					rects: [{
							left: '100px',
							top: '117px',
							width: 20,
							height: 20,
							color: 'blue'
						},
						{
							left: '203px',
							top: '117px',
							width: 20,
							height: 20,
							color: 'blue'
						},
					],
					introduction: '肩胛下角：肩胛骨的下端称为肩胛下角。受检者取直立位、两上肢自然下垂时，肩胛下角可作为第7或第8肋骨水平的标志，或相当于第8胸椎水平，为后胸壁计数肋骨的重要标志。',
					requireAll: true, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: false
				},
				{
					id: 7,
					text: "肩胛线",
					rects: [{
							left: '100px',
							top: '35px',
							width: 22,
							height: 200,
							color: 'blue'
						},
						{
							left: '204px',
							top: '35px',
							width: 22,
							height: 200,
							color: 'blue'
						}
					],
					introduction: '肩胛线：双臂自然下垂时通过左、右肩胛下角与后正中线平行的垂线。',
					requireAll: true, // 需要点击所有2个矩形
					timeout: 4000, // 4秒内完成
					drawingTask: true,
				},
				{
					id: 8,
					text: "后正中线",
					rects: [{
						left: '150px',
						top: '35px',
						width: 22,
						height: 200,
						color: 'blue'
					}],
					introduction: '后正中线（脊柱中线）：为通过椎骨棘突，或沿脊柱正中下行的垂直线。',
					requireAll: false, // 需要点击所有2个矩形
					timeout: 3000, // 3秒内完成
					drawingTask: true
				}
			]
		}
	})
	// 添加当前显示的视图索引
	const currentViewIndex = ref(VIEW_TYPES.FRONT);
	const allViewsCompleted = ref(false); // 是否所有视图都已完成
	// 新增动态图片数据
	const dynamicImages = {
		[VIEW_TYPES.FRONT]: visualDiagnosisImages.frontVideo,
		[VIEW_TYPES.BACK]: visualDiagnosisImages.behindVideo,
		[VIEW_TYPES.SIDE]: visualDiagnosisImages.sideVideo
	}
	// 获取当前显示的图片
	const getCurrentImage = () => {
		switch (currentViewIndex.value) {
			case VIEW_TYPES.FRONT:
				return visualDiagnosisImages.front;
			case VIEW_TYPES.BACK:
				return visualDiagnosisImages.behind;
			case VIEW_TYPES.SIDE:
				return visualDiagnosisImages.side;
			default:
				return visualDiagnosisImages.front;
		}
	};

	// 获取当前视图名称
	const getCurrentViewName = () => {
		switch (currentViewIndex.value) {
			case VIEW_TYPES.FRONT:
				return '前侧';
			case VIEW_TYPES.BACK:
				return '后侧';
			case VIEW_TYPES.SIDE:
				return '侧面';
			default:
				return '';
		}
	};
	// 控制动态图片显示状态
	const showDynamicImage = ref(false)
	const dynamicImageTimer = ref(null)

	// 获取当前动态图片
	const getCurrentDynamicImage = () => {
		return dynamicImages[currentViewIndex.value]
	}

	//显示动态图片
	const switchToNextView = () => {
		// 清除之前的定时器
		if (dynamicImageTimer.value) {
			clearTimeout(dynamicImageTimer.value)
		}

		// 清除所有画线
		clearAllLines();

		// 重置区域状态
		resetAllAreaStates();

		// 如果是最后一个视图，标记为完成
		if (currentViewIndex.value === VIEW_TYPES.BACK) {
			allViewsCompleted.value = true
			uni.showToast({
				title: '视图任务已完成！',
				icon: 'success',
				duration: 2000
			})
			// 隐藏不需要的元素
			showSeat.value = false
			showSeatUp.value = false
			shouldHideCartIcon.value = true;
			showDrawingMode.value = false
			showStartDrawingButton.value = false
			// 只保留背景纯色、右侧导航栏和麦克风
			showPlainBackground.value = true
			return
		}

		// 切换到下一个视图
		currentViewIndex.value++

		// 显示新视图的动态图片
		showDynamicImage.value = true
		console.log(`开始播放${getCurrentViewName()}动态图`);

		// 5秒后切换为静态图片
		dynamicImageTimer.value = setTimeout(() => {
			showDynamicImage.value = false
			console.log(`${getCurrentViewName()}动态图播放结束，切换到静态图`);

			// 初始化新视图的状态
			initNoClickRects()
			showStartDrawingButton.value = false
			lines.value = []
			currentLine.value = null
			showDrawingMode.value = false
		}, 10000)
	}

	// 处理视诊点击
	const handleVisualDiagnosisClick = (item) => {
		if (!selectedPosition.value || !selectedCloth.value) {
			uni.showToast({
				title: '请先选择体位和穿衣条件',
				icon: 'none'
			})
			return
		}
		showWholeLung.value = false;
		showChestExpansion.value = false;
		showPleuraExpansion.value = false;
		showLungLowerBound.value = false;
		// 重置视图为前侧
		currentViewIndex.value = VIEW_TYPES.FRONT;
		allViewsCompleted.value = false;

		// 清除所有画线
		clearAllLines();

		// 显示正面动态图片
		showDynamicImage.value = true
		console.log('开始播放正面动态图');

		// 5秒后切换为静态图片
		setTimeout(() => {
			showDynamicImage.value = false
			console.log('正面动态图播放结束，切换到静态图');
		}, 10000)

		showPlainBackground.value = true
		shouldHideCartIcon.value = true;
		showSeatUp.value = false;
		handlePhyCheck(item)
	}
	watch(showDynamicImage, (newVal) => {
		console.log('showDynamicImage changed:', newVal)
	})

	watch(currentViewIndex, (newVal) => {
		console.log('currentViewIndex changed:', newVal)
		console.log('Current dynamic image:', getCurrentDynamicImage())
	})

	// 组件卸载时清除定时器
	onUnmounted(() => {
		if (dynamicImageTimer.value) {
			clearTimeout(dynamicImageTimer.value)
		}
	})
	// 计算属性：检查当前视图所有任务是否完成
	const isTaskCompleted = (index) => {
		const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || []
		if (index >= currentAreas.length) return false

		const areaId = currentAreas[index].id
		return areaClickStates.value[areaId]?.isCompleted || false
	}
	const currentViewTasksCompleted = computed(() => {
		const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || []
		return currentAreas.every((_, index) => isTaskCompleted(index))
	})
	// 画线相关状态
	const showStartDrawingButton = ref(false);

	const clickedRects = ref([]) //记录点击的矩形索引
	const lastClickTime = ref(0) //记录上次点击的时间
	const showRectHint = ref(false) //是否显示提示
	const rectHintText = ref('') //提示文本
	const rectHintTimer = ref(null) //提示定时器

	const currentExpectedArea = ref(null); // 当前期望用户点击的区域ID
	const lastVoiceResult = ref(''); // 上一次语音识别结果
	// 获取可点击的区域（非画线任务）
	const getClickableAreas = () => {
		const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || []
		return currentAreas.filter(area => !area.drawingTask)
	}

	// 获取需要画线的区域
	const getDrawingAreas = () => {
		const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || []
		return currentAreas.filter(area => area.drawingTask)
	}
	// 处理矩形框点击
	const handleRectClick = (areaId, rectIndex) => {
		if (!currentExpectedArea.value) {
			showRectHint.value = true;
			rectHintText.value = '请先说出要点击的位置';
			setTimeout(() => {
				showRectHint.value = false;
			}, 2000);
			return;
		}
		if (areaId != currentExpectedArea.value) {
			showRectHint.value = true;
			rectHintText.value = '选择错误，请重试';
			setTimeout(() => {
				showRectHint.value = false;
			}, 2000);
			return;
		}
		const currentViewData = visualDiagnosisRibs.value[currentViewIndex.value]
		if (!currentViewData) return

		const area = currentViewData.areas.find(a => a.id === areaId)
		if (!area || area.drawingTask) return // 如果是画线任务则忽略点击

		// 初始化当前区域的点击状态
		if (!areaClickStates.value[areaId]) {
			areaClickStates.value[areaId] = {
				clickedRects: new Set(),
				firstClickTime: null,
				isCompleted: false
			}
		}

		const state = areaClickStates.value[areaId]
		if (state.isCompleted) return

		// 记录点击
		state.clickedRects.add(rectIndex)
		if (state.firstClickTime === null) {
			state.firstClickTime = Date.now()
		}

		// 检查完成条件
		if (area.requireAll) {
			const now = Date.now()
			const timeElapsed = now - state.firstClickTime

			if (state.clickedRects.size === area.rects.length && timeElapsed <= area.timeout) {
				completeArea(areaId, area);
				currentExpectedArea.value = null;
			} else if (timeElapsed > area.timeout) {
				resetAreaState(areaId)
				showRectHint.value = true; // 显示提示框
				rectHintText.value = `超时！请在${area.timeout/1000}秒内点击所有${area.text}的位置`; // 设置提示文本
			}
		} else {
			completeArea(areaId, area);
			currentExpectedArea.value = null;
		}
	}
	// 新增检查所有任务是否完成的函数
	const checkIfAllTasksCompleted = () => {
		const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || [];
		const allCompleted = currentAreas.every(area =>
			areaClickStates.value[area.id]?.isCompleted
		);

		if (allCompleted) {
			uni.showToast({
				title: `${getCurrentViewName()}任务已完成！`,
				icon: 'success',
				duration: 2000
			});

			// 立即切换到下一个视图
			setTimeout(() => {
				switchToNextView();
			}, 1000);
		}
	};
	const showIntroductionPopup = ref(false)
	const currentIntroduction = ref('')
	// 标记区域完成
	const completeArea = (areaId, area) => {
		// 1. 找到任务索引并标记完成
		const taskIndex = visualDiagnosisRibs.value[currentViewIndex.value].areas.findIndex(a => a.id ===
			areaId);
		if (taskIndex !== -1) {
			completeTask(taskIndex);
		}

		// 2. 更新区域状态为"已完成"
		areaClickStates.value[areaId].isCompleted = true;

		// 3. 显示完成提示
		showRectHint.value = true;
		rectHintText.value = `已完成 ${area.text} 的标注`;
		clearTimeout(rectHintTimer.value);
		rectHintTimer.value = setTimeout(() => {
			showRectHint.value = false;
		}, 1000);
		currentIntroduction.value = area.introduction
		showIntroductionPopup.value = true
		// 4. 检查是否是当前视图的最后一个任务
		checkIfAllTasksCompleted();
	};
	// 重置区域点击状态（超时或重新操作时）
	const resetAreaState = (areaId) => {
		if (!areaClickStates.value[areaId]) return;

		// 清除该区域相关的线条
		lines.value = lines.value.filter(line =>
			!areaClickStates.value[areaId].lines.some(
				areaLine => areaLine.timestamp === line.timestamp
			)
		);

		// 重置区域状态
		areaClickStates.value[areaId] = {
			lines: [],
			firstLineTime: null,
			isCompleted: false
		};

		// 重绘画布
		nextTick(() => {
			redrawCanvas();
		});
	};
	const scoreCriteria = ref([{
			text: "胸廓两侧对称，胸廓隆起，呈桶状胸，前后径与左右径比例约为1 ： 1，无畸形、无凹陷",
			keywords: ["胸廓两侧对称", "桶状胸", "前后径与左右径比例", "一比一", "无畸形", "无凹陷"]
		},
		{
			text: "皮肤色泽正常，无皮疹、无溃疡、无窦道、无瘢痕、无色素沉着、无皮下出血点、无蜘蛛痣、无静脉曲张、肋间隙彭隆",
			keywords: ["皮肤色泽正常", "无皮疹", "无溃疡", "无窦道", "无瘢痕", "无色素沉着", "无皮下出血点", "无蜘蛛痣", "无静脉曲张", "肋间隙彭隆"]
		},
		{
			text: "腹式呼吸，呼吸表浅，频率为25次/分，节律正常",
			keywords: ["腹式呼吸", "呼吸表浅", "频率", "25次每分", "节律正常"]
		}
	]);
	const userScore = ref(0);
	const maxScore = ref(3);
	// 检查语音结果并评分
	const checkVoiceResult = () => {
		if (!allViewsCompleted.value || !voiceResult.value) return;

		let score = 0;
		const userText = voiceResult.value.toLowerCase();

		scoreCriteria.value.forEach(criterion => {
			const hasMatch = criterion.keywords.some(keyword =>
				userText.includes(keyword.toLowerCase())
			);
			if (hasMatch) score++;
		});

		userScore.value = score;
		uni.showToast({
			title: `得分: ${score}/${maxScore.value}`,
			icon: 'none',
			success: () => {
				// 重置状态
				setTimeout(() => {
					voiceResult.value = "";
				}, 1000);
			}
		});
	};
	// 添加隐藏介绍弹窗的函数
	const hideIntroductionPopup = () => {
		showIntroductionPopup.value = false;
	};
	const isShowOverlay = ref(true)
	// 添加调试watch
	watch([isShowOverlay, showVisualDiagnosis], ([newOverlay, newVisual]) => {
		console.log('状态变化:', {
			isShowOverlay: newOverlay,
			showVisualDiagnosis: newVisual
		})
	})
	//触诊
	const palpation = ref([{
			id: 1,
			type: 'icon-yiliaofuwu',
			text: '胸廓扩张度'
		},
		{
			id: 2,
			type: 'icon-yiliaofuwu',
			text: '胸膜摩擦感'
		},
		{
			id: 3,
			type: 'icon-yiliaofuwu',
			text: '触觉语颤'
		},
	])
	//叩诊
	const percussion = ref([{
			id: 1,
			type: 'icon-yiliaofuwu',
			text: '全肺叩诊'
		},
		{
			id: 2,
			type: 'icon-yiliaofuwu',
			text: '肺下界叩诊'
		},
		{
			id: 3,
			type: 'icon-yiliaofuwu',
			text: '肺下界移动度'
		},
	])
	//听诊
	const auscultation = ref([{
			id: 1,
			type: 'icon-yiliaofuwu',
			text: '全肺听诊'
		},
		{
			id: 2,
			type: 'icon-yiliaofuwu',
			text: '胸膜摩擦音'
		},
		{
			id: 3,
			type: 'icon-yiliaofuwu',
			text: '听觉语颤'
		},
	])

	const bgImage = ref(bgImages.wardDefault);

	const IMG_URL = {
		DEFAULT: 'https://i.postimg.cc/nzQ9F9jz/image-png-2.png',
		PROJECT1: 'https://i.postimg.cc/XNP18xt0/image-png-5.png', //空背景
	}

	// 预加载图片（优化切换流畅度）
	const preloadImages = () => {
		new Image().src = IMG_URL.DEFAULT;
		new Image().src = IMG_URL.PROJECT1;
		// new Image().src = IMG_URL.PROJECT2;
	};

	// 功能按钮点击
	const carryOutProject = (item) => {
		// 每次点击测量按钮都恢复默认背景和隐藏 seat/seat-up
		bgImage.value = bgImages.wardDefault;
		showSeat.value = false;
		showSeatUp.value = false;
		if (isMeasuring.value) {
			uni.showToast({
				title: '测量中，请稍候...',
				icon: 'none',
				duration: 1500
			})
			return
		}
		if (showMeasurementResult.value) {
			hideBubble();
		}
		// 记录当前功能类型
		currentFunctionType.value = item.id
		// 如果有已锁定物品且类型匹配，直接测量
		const locked = dragItems.value.find(i => i.isLocked)
		if (locked && functionTypeMap[item.id] === locked.type) {
			startMeasurementByType(locked.type)
			return
		}
		// 否则打开医疗车弹窗并显示所有物品
		overlayVisible.value = true
		updateVisibleItems()
		// 提示拖动对应物品
		uni.showToast({
			title: '请拖动对应物品到指定位置',
			icon: 'none',
			duration: 2000
		})
	}

	// 统一测量入口
	function startMeasurementByType(type) {
		if (type === 'thermometer') {
			startTemperatureMeasurement()
		} else if (type === 'bloodPressure') {
			// 血压计测量流程
			isMeasuring.value = true
			showMeasurementCountdown.value = false // 血压计测量不显示倒计时
			measurementResult.value = '血压测量中...'
			showMeasurementResult.value = true
			setTimeout(() => {
				isMeasuring.value = false
				measurementResult.value = '血压：120/80 mmHg'
				setTimeout(() => {
					// 解锁所有物品并回到初始位置
					dragItems.value.forEach(item => {
						item.isLocked = false
						item.x = item.initialX
						item.y = item.initialY
						item.visible = false
					})
					lockedItemId.value = null
					currentFunctionType.value = null
					overlayVisible.value = false
				}, 3000)
			}, 3000) // 立即显示结果
		}
	}

	//体温测量
	function startTemperatureMeasurement() {
		isMeasuring.value = true
		showMeasurementCountdown.value = false // 体温测量不显示倒计时
		measurementResult.value = `体温测量中，请稍候...`
		bubbleHiding.value = false
		showMeasurementResult.value = true
		const showTemperatureResult = () => {
			isMeasuring.value = false
			const baseTemp = 36.5
			const randomOffset = Math.random() * 1.0 - 0.3
			const temp = (baseTemp + randomOffset).toFixed(1)
			// measurementResult.value = `患者体温：${temp}°C`
			// 弹出体温图片和填空题弹窗（固定为37.8）
			measurementResult.value = `患者体温：37.8°C`
			tempQuizCorrectValue.value = '37.8'
			showTempQuizDialog.value = true
			tempQuizUserInput.value = ''
			tempQuizResult.value = ''
			tempQuizImage.value = quizImages.tempReading;
			setTimeout(() => {
				// 解锁所有物品并回到初始位置
				dragItems.value.forEach(item => {
					item.isLocked = false
					item.x = item.initialX
					item.y = item.initialY
					item.visible = false
				})
				isTempMeasureMode.value = false
				isThermometerReady.value = false
				lockedItemId.value = null
				currentFunctionType.value = null
				overlayVisible.value = false
			}, 3000)
		}
		setTimeout(showTemperatureResult, 0) // 立即显示结果
	}

	// 开始脉搏呼吸测量
	const startPulseBreathMeasurement = () => {
		isPulseBreathMeasuring.value = true
		isMeasuring.value = true // 保持测量中状态
		showMeasurementCountdown.value = true // 脉搏呼吸测量显示倒计时
		measurementCountdown.value = 60 // 脉搏倒计时1分钟
		showMeasurementResult.value = true
		measurementResult.value = '脉搏呼吸测量中...'
		// 显示题目页面并重置题目状态
		showTitleContainer.value = true
		resetAllQuestions()
		// 60秒后显示脉搏结果
		setTimeout(() => {
			const pulseRate = Math.floor(Math.random() * 20) + 60 // 60-80次/分
			pulseResult.value = `脉搏：${pulseRate}次/分`
			showPulseResult.value = true
			measurementResult.value = pulseResult.value
			showMeasurementCountdown.value = false // 只显示1分钟倒计时，后续不再显示
			// 再过3秒显示呼吸结果
			setTimeout(() => {
				const breathRate = Math.floor(Math.random() * 6) + 16 // 16-22次/分
				breathResult.value = `呼吸：${breathRate}次/分`
				showBreathResult.value = true
				measurementResult.value = `${pulseResult.value}\n${breathResult.value}`
				// 3秒后重置
				setTimeout(() => {
					isPulseBreathMeasuring.value = false
					showMeasurementResult.value = false
					showPulseResult.value = false
					showBreathResult.value = false
					pulseResult.value = ''
					breathResult.value = ''
					measurementResult.value = ''
					overlayVisible.value = false
					// 隐藏题目页面
					showTitleContainer.value = false
				}, 3000)
			}, 3000) // 3秒
		}, 60000) // 1分钟
	}

	// 语音识别相关变量
	const voiceResult = ref('') // 语音识别结果
	const isRecording = ref(false) // 是否正在录音
	let recorderManager = null
	let audioFilePath = null
	// 小程序端（含PC开发者工具）分帧PCM累积
	let wxPcmChunks = []
	let wxPcmTotalLen = 0
	const wxSampleRate = 16000

	// 初始化微信同声传译插件
	onMounted(() => {
		// 设置默认背景
		bgImage.value = IMG_URL.DEFAULT;

		// 初始化所有状态
		showMeasurementResult.value = false;
		isMeasuring.value = false;

		// 环境检测
		console.log('=== 环境检测 ===')
		console.log('当前平台:', process.env.UNI_PLATFORM)
		console.log('是否在微信小程序环境:', process.env.UNI_PLATFORM === 'mp-weixin')

		// 检查录音管理器是否可用
		try {
			recorderManager = uni.getRecorderManager()
			console.log('录音管理器初始化:', recorderManager)

			if (!recorderManager) {
				console.error('录音管理器初始化失败')
				return
			}

			// 立即绑定录音事件监听
			console.log('开始绑定录音事件监听...')

			// 使用函数声明方式绑定回调
			const onStartHandler = function(res) {
				console.log('=== 录音开始回调被触发 ===')
				console.log('录音开始回调参数:', res)
				console.log('录音开始时间:', new Date().toISOString())

				isRecording.value = true
				voiceResult.value = "正在聆听..."
				console.log('录音状态已更新，isRecording:', isRecording.value)

				// 添加录音过程监控
				console.log('开始监控录音过程...')
				const recordingMonitor = setInterval(() => {
					console.log('录音进行中... isRecording:', isRecording.value)
					if (!isRecording.value) {
						console.log('录音已结束，停止监控')
						clearInterval(recordingMonitor)
					}
				}, 1000) // 每秒检查一次

				// 检查录音管理器状态
				console.log('录音开始后检查录音管理器:')
				console.log('recorderManager对象:', recorderManager)
				console.log('recorderManager.onStop:', recorderManager.onStop)
				console.log('recorderManager.onError:', recorderManager.onError)
			}

			const onStopHandler = function(res) {
				console.log('=== 录音结束回调被触发 ===')
				console.log('录音结束回调参数:', res)
				console.log('录音文件路径:', res.tempFilePath)
				console.log('录音时长:', res.duration, 'ms')
				console.log('文件大小:', res.fileSize, 'bytes')
				console.log('录音结束回调被触发的时间:', new Date().toISOString())

				isRecording.value = false
				audioFilePath = res.tempFilePath

				// 验证文件是否存在
				if (res.tempFilePath) {
					console.log('录音文件路径有效，开始上传到云函数')
					// 统一走带有容器探测与格式适配的上传函数
					uploadToUniCloud(audioFilePath)
				} else {
					console.error('录音文件路径无效')
					voiceResult.value = '录音文件生成失败'
				}
			}

			const onErrorHandler = (err) => {
				console.error('=== 录音错误回调被触发 ===')
				console.error('录音错误回调参数:', err)
				console.error('录音错误详情:', {
					errMsg: err.errMsg,
					errCode: err.errCode,
					message: err.message
				})
				isRecording.value = false
				voiceResult.value = '录音失败，请重试'
			}

			const onInterruptionBeginHandler = () => {
				console.log('=== 录音被中断 ===')
				console.log('录音中断时间:', new Date().toISOString())
				isRecording.value = false
				voiceResult.value = '录音被中断'
			}

			const onInterruptionEndHandler = () => {
				console.log('=== 录音中断结束 ===')
				console.log('录音中断结束时间:', new Date().toISOString())
			}

			// 绑定回调（使用注册方式而非赋值）
			recorderManager.onStart(onStartHandler)
			recorderManager.onStop(onStopHandler)
			recorderManager.onError(onErrorHandler)
			recorderManager.onInterruptionBegin(onInterruptionBeginHandler)
			recorderManager.onInterruptionEnd(onInterruptionEndHandler)
			// 分帧PCM（仅小程序有效）
			if (typeof recorderManager.onFrameRecorded === 'function') {
				recorderManager.onFrameRecorded((res) => {
					try {
						const chunk = new Uint8Array(res.frameBuffer)
						wxPcmChunks.push(chunk)
						wxPcmTotalLen += chunk.length
					} catch (e) {
						console.warn('onFrameRecorded 处理失败:', e)
					}
				})
			}

			console.log('=== 录音回调绑定完成 ===')
			console.log('onStop回调已绑定:', typeof onStopHandler)

			console.log('录音事件监听器已绑定完成')

			// 验证回调是否绑定成功
			console.log('验证回调绑定:')
			console.log('onStart回调类型:', typeof recorderManager.onStart)
			console.log('onStop回调类型:', typeof recorderManager.onStop)
			console.log('onError回调类型:', typeof recorderManager.onError)

			// 测试回调是否可调用
			try {
				console.log('测试回调是否可调用...')
				if (typeof recorderManager.onStart === 'function') {
					console.log('onStart回调是可调用的函数')
				} else {
					console.error('onStart回调不是函数:', recorderManager.onStart)
				}
			} catch (e) {
				console.error('测试回调时出错:', e)
			}

		} catch (error) {
			console.error('录音管理器初始化异常:', error)
			return
		}
	});
	// 语音按钮点击事件
	const handleVoiceButtonClick = () => {
		console.log('语音按钮被点击')

		// 关闭气泡
		if (showMeasurementResult.value) {
			hideBubble();
		}

		if (isRecording.value) {
			// 如果正在录音，则停止录音
			console.log('正在录音，执行停止录音操作')
			stopRecording()
		} else {
			// 如果没有在录音，则开始录音
			console.log('没有在录音，执行开始录音操作')
			startRecording()
		}
	}

	// 上传音频到云函数
	const uploadAudioToCloud = (filePath) => {
		console.log('=== 开始上传音频到云函数 ===')
		console.log('文件路径:', filePath)

		// 先读取音频文件为base64
		uni.getFileSystemManager().readFile({
			filePath: filePath,
			encoding: 'base64',
			success: (res) => {
				console.log('=== 音频文件读取成功 ===')
				console.log('音频文件大小:', res.data.length, '字节')
				console.log('音频数据前100字符:', res.data.substring(0, 100))

				console.log('=== 开始调用云函数 ===')
				// 调用云函数
				uniCloud.callFunction({
					name: 'speech-recognition',
					data: {
						audioBase64: res.data,
						audioFormat: 'mp3' // 传递mp3格式参数
					}
				}).then(result => {
					console.log('=== 云函数调用成功 ===')
					console.log('云函数返回结果:', result)
					console.log('云函数返回code:', result.result.code)
					console.log('云函数返回message:', result.result.message)

					if (result.result.code === 0) {
						const recognizedText = result.result.data.text || '识别结果为空'
						console.log('=== 语音识别成功 ===')
						console.log('识别结果:', recognizedText)
						voiceResult.value = recognizedText

						// 强制更新视图
						nextTick(() => {
							console.log('voiceResult已更新:', voiceResult.value)
						})
					} else {
						const errorMsg = `识别失败: ${result.result.message}`
						console.log('=== 语音识别失败 ===')
						console.log('错误信息:', errorMsg)
						voiceResult.value = errorMsg
					}
				}).catch(err => {
					console.error('=== 云函数调用失败 ===')
					console.error('云函数调用失败:', err)
					console.error('错误详情:', {
						message: err.message,
						stack: err.stack,
						errMsg: err.errMsg,
						errCode: err.errCode
					})

					let errorMsg = '语音识别服务异常，请重试'

					// 根据错误类型提供更具体的错误信息
					if (err.message) {
						if (err.message.includes('not found')) {
							errorMsg = '云函数未找到，请检查部署'
						} else if (err.message.includes('timeout')) {
							errorMsg = '云函数调用超时'
						} else if (err.message.includes('network')) {
							errorMsg = '网络连接异常'
						} else {
							errorMsg = `调用失败: ${err.message}`
						}
					}

					console.log('设置错误信息:', errorMsg)
					voiceResult.value = errorMsg

					// 显示错误提示
					uni.showToast({
						title: errorMsg,
						icon: 'none',
						duration: 3000
					})
				})
			},
			fail: (err) => {
				console.error('=== 音频文件读取失败 ===')
				console.error('读取音频文件失败:', err)
				console.error('错误详情:', {
					message: err.message,
					errMsg: err.errMsg
				})
				const errorMsg = '音频文件读取失败'
				console.log('设置错误信息:', errorMsg)
				voiceResult.value = errorMsg
			}
		})
	}
	// 语音按钮点击事件
	const testButtonClick = () => {
		console.log('语音按钮被点击')
	}

	// 语音按钮触摸事件
	const testTouchStart = () => {
		console.log('=== 语音按钮触摸开始 ===')
		touchStart()
	}

	const testTouchEnd = () => {
		console.log('=== 语音按钮触摸结束 ===')
		touchEnd()
	}
	// 检测是否为PC端
	const isPC = () => {
		const systemInfo = uni.getSystemInfoSync()
		return !systemInfo.platform.includes('mobile')
	}

	// H5 / PC 环境检测（用于启用 WebAudio WAV 录音方案）
	function isH5OrDesktop() {
		try {
			const sys = uni.getSystemInfoSync()
			const platform = (sys.platform || '').toLowerCase()
			const isDevTools = platform === 'devtools'
			const isDesktop = platform.includes('windows') || platform.includes('mac') || platform.includes(
				'desktop')
			const isH5 = (process.env.UNI_PLATFORM || '').includes('h5')
			return isH5 || isDesktop || isDevTools
		} catch (e) {
			return false
		}
	}

	// WebAudio WAV 录音（仅 H5）
	let waAudioContext = null
	let waMediaStream = null
	let waSource = null
	let waProcessor = null
	let waBuffers = []
	let waInputSampleRate = 44100
	const targetSampleRate = 16000
	let waActive = false

	function initWebAudioIfNeeded() {
		if (typeof window === 'undefined') return false
		if (!window.AudioContext && !window.webkitAudioContext) return false
		return true
	}

	function startWavRecord() {
		return new Promise(async (resolve, reject) => {
			try {
				if (!initWebAudioIfNeeded() || !navigator.mediaDevices || !navigator.mediaDevices
					.getUserMedia) {
					return reject(new Error('WebAudio或麦克风不可用'))
				}
				waAudioContext = new(window.AudioContext || window.webkitAudioContext)({
					sampleRate: 48000
				})
				waInputSampleRate = waAudioContext.sampleRate
				waMediaStream = await navigator.mediaDevices.getUserMedia({
					audio: {
						sampleRate: waInputSampleRate,
						channelCount: 1
					}
				})
				waSource = waAudioContext.createMediaStreamSource(waMediaStream)
				const bufferSize = 4096
				waProcessor = waAudioContext.createScriptProcessor(bufferSize, 1, 1)
				waProcessor.onaudioprocess = (e) => {
					const input = e.inputBuffer.getChannelData(0)
					waBuffers.push(new Float32Array(input))
				}
				waSource.connect(waProcessor)
				waProcessor.connect(waAudioContext.destination)
				waActive = true
				resolve(true)
			} catch (err) {
				reject(err)
			}
		})
	}

	function stopWavRecordAndGetBase64() {
		return new Promise(async (resolve, reject) => {
			try {
				if (!waActive) {
					return reject(new Error('WebAudio未启动'))
				}
				if (typeof Blob === 'undefined' || typeof FileReader === 'undefined') {
					return reject(new Error('当前环境不支持Blob/FileReader'))
				}
				if (waProcessor) waProcessor.disconnect()
				if (waSource) waSource.disconnect()
				if (waMediaStream) waMediaStream.getTracks().forEach(t => t.stop())
				if (waAudioContext && waAudioContext.state !== 'closed') await waAudioContext.close()
				waActive = false

				// 拼接 Float32
				let totalLength = waBuffers.reduce((acc, cur) => acc + cur.length, 0)
				const floatData = new Float32Array(totalLength)
				let offset = 0
				for (const buf of waBuffers) {
					floatData.set(buf, offset)
					offset += buf.length
				}
				waBuffers = []

				// 重采样到 16000Hz
				const resampled = downsampleBuffer(floatData, waInputSampleRate, targetSampleRate)
				// 转为PCM16
				const pcm16 = floatTo16BitPCM(resampled)
				// 封装 WAV (RIFF/WAVE PCM)
				const wavBuffer = buildWavFile(pcm16, targetSampleRate, 1)

				// 转 base64
				const blob = new Blob([wavBuffer], {
					type: 'audio/wav'
				})
				const reader = new FileReader()
				reader.onloadend = () => {
					const dataUrl = reader.result // data:audio/wav;base64,....
					const base64 = String(dataUrl).split(',')[1] || ''
					resolve(base64)
				}
				reader.onerror = reject
				reader.readAsDataURL(blob)
			} catch (err) {
				reject(err)
			}
		})
	}

	// 开始录音
	const touchStart = () => {
		console.log('=== touchStart 被调用 ===')

		// 关闭气泡
		if (showMeasurementResult.value) {
			hideBubble();
		}

		if (!recorderManager) {
			console.error('recorderManager 未初始化')
			uni.showToast({
				title: '录音初始化失败',
				icon: 'none'
			})
			return
		}

		console.log('recorderManager 已初始化，开始检查权限')
		console.log('recorderManager 对象:', recorderManager)
		console.log('recorderManager.onStart:', recorderManager.onStart)

		// 检查录音权限
		uni.getSetting({
			success: (res) => {
				console.log('权限检查成功:', res.authSetting)
				console.log('录音权限状态:', res.authSetting['scope.record'])
				console.log('所有权限状态:', res.authSetting)

				if (!res.authSetting['scope.record']) {
					console.log('没有录音权限，申请权限')
					// 没有录音权限，申请权限
					uni.authorize({
						scope: 'scope.record',
						success: () => {
							console.log('录音权限申请成功')
							startRecording()
						},
						fail: (err) => {
							console.error('录音权限申请失败:', err)
							console.error('权限申请失败详情:', {
								errMsg: err.errMsg,
								errCode: err.errCode
							})
							uni.showToast({
								title: '请授权使用麦克风',
								icon: 'none'
							})
						}
					})
				} else {
					console.log('已有录音权限，直接开始录音')
					// 已有权限，直接开始录音
					startRecording()
				}
			},
			fail: (err) => {
				console.error('获取设置失败:', err)
				console.error('获取设置失败详情:', {
					errMsg: err.errMsg,
					errCode: err.errCode
				})
				// 权限检查失败，尝试直接录音
				console.log('权限检查失败，尝试直接录音')
				startRecording()
			}
		})
	}


	// 开始录音函数
	const startRecording = async () => {
		console.log('=== 开始录音函数被调用 ===')

		if (!recorderManager) {
			console.error('recorderManager 未初始化')
			return
		}

		console.log('recorderManager 状态:', recorderManager)

		// 确认回调绑定状态
		console.log('录音前回调绑定状态:')
		console.log('onStart回调类型:', typeof recorderManager.onStart)
		console.log('onStop回调类型:', typeof recorderManager.onStop)
		console.log('onError回调类型:', typeof recorderManager.onError)

		try {
			// 使用MP3格式录音，兼容讯飞API
			const sys = uni.getSystemInfoSync()
			const platform = (sys.platform || '').toLowerCase()
			const isDevTools = platform === 'devtools'
			const isDesktop = platform.includes('windows') || platform.includes('mac') || platform
				.includes(
					'desktop')
			const isH5 = (process.env.UNI_PLATFORM || '').includes('h5')

			// 默认移动端参数（MP3/16k/单声道）
			let primaryOptions = {
				format: 'mp3',
				sampleRate: 16000,
				numberOfChannels: 1,
				encodeBitRate: 32000
			}
			// 仅 H5 启用 WAV/PCM16；小程序(含PC开发者工具)保持 MP3
			if (isH5) {
				primaryOptions = {
					format: 'wav',
					sampleRate: 16000,
					numberOfChannels: 1
				}
				uni.showToast({
					title: 'H5已启用WAV/PCM录音',
					icon: 'none'
				})
			}

			console.log('录音参数(首选):', primaryOptions)
			console.log('开始调用 recorderManager.start()...')

			// 仅 H5 浏览器启用 WebAudio 录 WAV/PCM16（需 window/navigator 支持）
			const canWebAudioStart = (typeof window !== 'undefined') && (typeof navigator !==
					'undefined') && !!
				navigator.mediaDevices && isH5
			if (canWebAudioStart) {
				// 启动 WebAudio 采集
				try {
					await startWavRecord()
					isRecording.value = true
					voiceResult.value = '正在聆听...'
					return
				} catch (e) {
					console.warn('WebAudio 启动失败，回退小程序录音:', e)
				}
			}

			// 非H5或WebAudio失败则走小程序录音（小程序启用分帧PCM）
			try {
				if ((process.env.UNI_PLATFORM || '') === 'mp-weixin') {
					wxPcmChunks = []
					wxPcmTotalLen = 0
					const mpOpts = {
						...primaryOptions,
						frameSize: 16
					}
					recorderManager.start(mpOpts)
				} else {
					recorderManager.start(primaryOptions)
				}
			} catch (e) {
				console.error('启动小程序录音失败:', e)
				throw e
			}

			console.log('录音启动命令已发送')

			// 立即检查录音状态
			setTimeout(() => {
				console.log('录音启动后立即检查 - isRecording:', isRecording.value)
				console.log('recorderManager 对象状态:', recorderManager)

				// 检查是否有任何错误
				try {
					console.log('检查录音管理器方法:')
					console.log('start方法:', typeof recorderManager.start)
					console.log('stop方法:', typeof recorderManager.stop)
					console.log('pause方法:', typeof recorderManager.pause)
					console.log('resume方法:', typeof recorderManager.resume)
				} catch (e) {
					console.error('检查录音管理器方法时出错:', e)
				}
			}, 100)

			// 检查录音状态，若 600ms 内仍未开始，尝试回退到 MP3
			setTimeout(() => {
				console.log('录音启动后状态检查 - isRecording:', isRecording.value)
				console.log('录音启动后状态检查 - voiceResult:', voiceResult.value)

				// 如果没有触发onStart回调，可能是权限或环境问题
				if (!isRecording.value) {
					console.warn('录音未开始，尝试回退到MP3参数')
					try {
						recorderManager.stop()
					} catch (e) {}
					setTimeout(() => {
						const fallbackOptions = {
							format: 'mp3',
							sampleRate: 16000,
							numberOfChannels: 1,
							encodeBitRate: 32000
						}
						console.log('回退录音参数:', fallbackOptions)
						try {
							recorderManager.start(fallbackOptions)
						} catch (e) {
							console.error('回退启动失败:', e)
						}
					}, 100)

					// 尝试检查录音权限
					uni.getSetting({
						success: (res) => {
							console.log('当前录音权限状态:', res.authSetting['scope.record'])
							if (!res.authSetting['scope.record']) {
								console.error('录音权限未授权')
								uni.showToast({
									title: '请授权录音权限',
									icon: 'none'
								})
							} else {
								console.log('录音权限已授权，但回调未触发')
								console.log('尝试手动触发录音开始回调...')

								// 手动触发回调测试
								try {
									if (typeof recorderManager.onStart ===
										'function') {
										console.log('手动调用onStart回调进行测试...')
										recorderManager.onStart({
											test: true
										})
									}
								} catch (e) {
									console.error('手动调用回调时出错:', e)
								}
							}
						}
					})
				}
			}, 2000) // 延长检查时间到2秒

		} catch (error) {
			console.error('录音启动失败:', error)
			console.error('错误详情:', {
				message: error.message,
				stack: error.stack
			})
		}
	}
	// 停止录音函数
	const stopRecording = () => {
		console.log('=== stopRecording 被调用 ===')
		console.log('当前录音状态 isRecording:', isRecording.value)

		const sys = uni.getSystemInfoSync()
		const platform = (sys.platform || '').toLowerCase()
		const isDevTools = platform === 'devtools'
		const isDesktop = platform.includes('windows') || platform.includes('mac') || platform.includes(
			'desktop')
		const isH5 = (process.env.UNI_PLATFORM || '').includes('h5')

		if (waActive && (typeof window !== 'undefined') && (typeof Blob !== 'undefined') && (
				typeof FileReader !==
				'undefined')) {
			// WebAudio 流停止并上传
			stopWavRecordAndGetBase64().then((wavBase64) => {
				console.log('WebAudio WAV base64 长度:', wavBase64.length)
				isRecording.value = false
				// 直接调用云函数（raw）
				uniCloud.callFunction({
					name: 'speech-recognition',
					data: {
						audioBase64: wavBase64,
						audioFormat: 'raw'
					}
				}).then(result => {
					console.log('uniCloud返回结果:', result)
					if (result.result.code === 0) {
						voiceResult.value = result.result.data.text || '识别结果为空'
					} else {
						voiceResult.value = `识别失败: ${result.result.message}`
					}
				}).catch(err => {
					console.error('uniCloud调用失败:', err)
					voiceResult.value = '语音识别服务异常，请重试'
				})
			}).catch(err => {
				console.error('WebAudio 停止失败:', err)
				isRecording.value = false
				// 失败则回退为停止小程序原生录音（若仍在录制）
				try {
					if (recorderManager) recorderManager.stop()
				} catch (e) {}
				voiceResult.value = 'PC录音失败，已回退原生录音'
			})
			return
		}

		if (recorderManager) {
			console.log('开始停止录音...')
			recorderManager.stop()
			console.log('录音停止命令已发送')
		} else {
			console.warn('recorderManager 未初始化，无法停止录音')
		}
	}
	// 结束录音
	const touchEnd = () => {
		console.log('=== touchEnd 被调用 ===')
		console.log('当前录音状态 isRecording:', isRecording.value)

		const sys = uni.getSystemInfoSync()
		const platform = (sys.platform || '').toLowerCase()
		const isDevTools = platform === 'devtools'
		const isDesktop = platform.includes('windows') || platform.includes('mac') || platform.includes(
			'desktop')
		const isH5 = (process.env.UNI_PLATFORM || '').includes('h5')

		if (waActive && (typeof window !== 'undefined') && (typeof Blob !== 'undefined') && (
				typeof FileReader !==
				'undefined')) {
			// WebAudio 流停止并上传
			stopWavRecordAndGetBase64().then((wavBase64) => {
				console.log('WebAudio WAV base64 长度:', wavBase64.length)
				isRecording.value = false
				// 直接调用云函数（raw）
				uniCloud.callFunction({
					name: 'speech-recognition',
					data: {
						audioBase64: wavBase64,
						audioFormat: 'raw'
					}
				}).then(result => {
					console.log('uniCloud返回结果:', result)
					if (result.result.code === 0) {
						voiceResult.value = result.result.data.text || '识别结果为空'
					} else {
						voiceResult.value = `识别失败: ${result.result.message}`
					}
				}).catch(err => {
					console.error('uniCloud调用失败:', err)
					voiceResult.value = '语音识别服务异常，请重试'
				})
			}).catch(err => {
				console.error('WebAudio 停止失败:', err)
				isRecording.value = false
				// 失败则回退为停止小程序原生录音（若仍在录制）
				try {
					if (recorderManager) recorderManager.stop()
				} catch (e) {}
				voiceResult.value = 'PC录音失败，已回退原生录音'
			})
			return
		}

		if (recorderManager) {
			console.log('开始停止录音...')
			recorderManager.stop()
			console.log('录音停止命令已发送')
		} else {
			console.warn('recorderManager 未初始化，无法停止录音')
		}
	}

	function downsampleBuffer(buffer, inSampleRate, outSampleRate) {
		if (outSampleRate === inSampleRate) return buffer
		const ratio = inSampleRate / outSampleRate
		const newLen = Math.floor(buffer.length / ratio)
		const result = new Float32Array(newLen)
		let offsetResult = 0
		let offsetBuffer = 0
		while (offsetResult < result.length) {
			const nextOffsetBuffer = Math.round((offsetResult + 1) * ratio)
			// 简单平均法
			let accum = 0,
				count = 0
			for (let i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
				accum += buffer[i]
				count++
			}
			result[offsetResult] = accum / count
			offsetResult++
			offsetBuffer = nextOffsetBuffer
		}
		return result
	}

	function floatTo16BitPCM(float32Array) {
		const len = float32Array.length
		const buffer = new ArrayBuffer(len * 2)
		const view = new DataView(buffer)
		let offset = 0
		for (let i = 0; i < len; i++, offset += 2) {
			let s = Math.max(-1, Math.min(1, float32Array[i]))
			view.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true)
		}
		return new Uint8Array(buffer)
	}

	function buildWavFile(samplesUint8, sampleRate, numChannels) {
		const byteRate = sampleRate * numChannels * 2
		const blockAlign = numChannels * 2
		const dataSize = samplesUint8.length
		const buffer = new ArrayBuffer(44 + dataSize)
		const view = new DataView(buffer)

		// RIFF header
		writeString(view, 0, 'RIFF')
		view.setUint32(4, 36 + dataSize, true)
		writeString(view, 8, 'WAVE')
		// fmt chunk
		writeString(view, 12, 'fmt ')
		view.setUint32(16, 16, true) // PCM chunk size
		view.setUint16(20, 1, true) // PCM format = 1
		view.setUint16(22, numChannels, true)
		view.setUint32(24, sampleRate, true)
		view.setUint32(28, byteRate, true)
		view.setUint16(32, blockAlign, true)
		view.setUint16(34, 16, true) // bits per sample
		// data chunk
		writeString(view, 36, 'data')
		view.setUint32(40, dataSize, true)
		const out = new Uint8Array(buffer)
		out.set(samplesUint8, 44)
		return out
	}

	function writeString(view, offset, str) {
		for (let i = 0; i < str.length; i++) {
			view.setUint8(offset + i, str.charCodeAt(i))
		}
	}

	// 将 base64 字符串转为 Uint8Array
	function base64ToUint8Array(b64) {
		const binary = typeof atob !== 'undefined' ? atob(b64) : Buffer.from(b64, 'base64').toString('binary')
		const len = binary.length
		const bytes = new Uint8Array(len)
		for (let i = 0; i < len; i++) bytes[i] = binary.charCodeAt(i)
		return bytes
	}

	// Uint8Array 转 base64 字符串
	function uint8ToBase64(u8) {
		if (typeof btoa !== 'undefined') {
			let str = ''
			const chunk = 0x8000
			for (let i = 0; i < u8.length; i += chunk) {
				str += String.fromCharCode.apply(null, u8.subarray(i, i + chunk))
			}
			return btoa(str)
		} else {
			return Buffer.from(u8).toString('base64')
		}
	}

	// 将 WebM/Ogg base64 解码为 AudioBuffer，并转为 16k 单声道 WAV base64
	async function convertWebmBase64ToPcmWavBase64(b64) {
		if (typeof window === 'undefined') throw new Error('仅 H5 支持 WebAudio 解码')
		const audioCtx = new(window.AudioContext || window.webkitAudioContext)()
		const u8 = base64ToUint8Array(b64)
		const arrayBuf = u8.buffer.slice(u8.byteOffset, u8.byteOffset + u8.byteLength)
		const audioBuffer = await new Promise((resolve, reject) => {
			audioCtx.decodeAudioData(arrayBuf, resolve, reject)
		})
		await audioCtx.close()

		// 混合为单声道
		const ch = Math.min(2, audioBuffer.numberOfChannels || 1)
		let mono = new Float32Array(audioBuffer.length)
		if (ch === 1) {
			mono = audioBuffer.getChannelData(0)
		} else {
			const c0 = audioBuffer.getChannelData(0)
			const c1 = audioBuffer.getChannelData(1)
			mono = new Float32Array(audioBuffer.length)
			for (let i = 0; i < audioBuffer.length; i++) mono[i] = (c0[i] + c1[i]) / 2
		}
		// 重采样至 16k
		const resampled = downsampleBuffer(mono, audioBuffer.sampleRate, targetSampleRate)
		const pcm16 = floatTo16BitPCM(resampled)
		const wav = buildWavFile(pcm16, targetSampleRate, 1)
		return uint8ToBase64(wav)
	}



	// 音频容器/编码类型探测（基于base64前缀）
	function detectAudioBase64Type(b64) {
		if (!b64 || b64.length < 4) return 'unknown'
		const head4 = b64.slice(0, 4)
		if (head4 === 'SUQz') return 'mp3' // ID3 -> MP3
		if (head4 === 'UklG') return 'pcm' // RIFF/WAV/PCM
		if (head4 === 'GkXf') return 'webm' // WebM/Matroska
		if (head4 === 'T2dn') return 'ogg' // Ogg/Opus
		return 'unknown'
	}

	// 统一的语音识别函数（PC端和手机端都使用uniCloud）
	const uploadToUniCloud = async (filePath) => {
		console.log('=== 上传音频到uniCloud进行识别 ===')
		console.log('文件路径:', filePath)

		// 先读取音频文件为base64
		uni.getFileSystemManager().readFile({
			filePath: filePath,
			encoding: 'base64',
			success: async (res) => {
				const audioBase64 = res.data
				console.log('音频文件读取成功，长度:', audioBase64.length)

				// 探测容器/编码，避免 WebM/Ogg 在PC端导致空识别
				let kind = detectAudioBase64Type(audioBase64)
				console.log('检测到音频容器/编码类型:', kind)
				// 若为 WebM/Ogg：
				// - H5 环境尝试前端转 WAV/PCM16
				// - 小程序/开发者工具直接把原始base64上传到云端，由云端FFmpeg转码
				if (kind === 'webm' || kind === 'ogg') {
					const isH5Env = (typeof window !== 'undefined') && (typeof navigator !==
						'undefined')
					if (isH5Env) {
						try {
							const converted = await convertWebmBase64ToPcmWavBase64(
								audioBase64)
							console.log('WebM/Ogg 已转 WAV/PCM16，长度:', converted.length)
							kind = 'pcm'
							await uniCloud.callFunction({
								name: 'speech-recognition',
								data: {
									audioBase64: converted,
									audioFormat: 'raw'
								}
							}).then(result => {
								console.log('uniCloud返回结果(转码后):', result)
								if (result.result.code === 0) {
									voiceResult.value = result.result.data.text ||
										'识别结果为空'
								} else {
									voiceResult.value =
										`识别失败: ${result.result.message}`
								}
							}).catch(err => {
								console.error('uniCloud调用失败(转码后):', err)
								voiceResult.value = '语音识别服务异常，请重试'
							})
							return
						} catch (e) {
							console.warn('WebM/Ogg 前端转 WAV 失败，改走云端转码:', e)
						}
					}
					// 非H5环境（小程序/PC开发者工具）：直接上传原始webm/ogg，让云端转码
					await uniCloud.callFunction({
						name: 'speech-recognition',
						data: {
							audioBase64,
							audioFormat: 'webm'
						}
					}).then(result => {
						console.log('uniCloud返回结果(云端转码):', result)
						if (result.result.code === 0) {
							voiceResult.value = result.result.data.text || '识别结果为空'
						} else {
							voiceResult.value = `识别失败: ${result.result.message}`
						}
					}).catch(err => {
						console.error('uniCloud调用失败(云端转码):', err)
						voiceResult.value = '语音识别服务异常，请重试'
					})
					return
				}
				const audioFormat = (kind === 'pcm') ? 'raw' : 'mp3'
				console.log('将以 audioFormat 发送:', audioFormat)

				// 统一调用 WebSocket v2 云函数
				uniCloud.callFunction({
					name: 'speech-recognition',
					data: {
						audioBase64,
						audioFormat
					}
				}).then(result => {
					console.log('uniCloud返回结果:', result)
					if (result.result.code === 0) {
						voiceResult.value = result.result.data.text || '识别结果为空'
					} else {
						voiceResult.value = `识别失败: ${result.result.message}`
					}
				}).catch(err => {
					console.error('uniCloud调用失败:', err)
					voiceResult.value = '语音识别服务异常，请重试'
				})
			},
			fail: (err) => {
				console.error('读取音频文件失败:', err)
				voiceResult.value = '音频文件读取失败'
			}
		})
	}

	// 修改录音结束回调，统一使用uniCloud
	const onStopHandler = async (res) => {
		console.log('=== 录音结束回调被触发 ===')
		console.log('录音结束回调参数:', res)
		console.log('录音文件路径:', res.tempFilePath)
		console.log('录音时长:', res.duration, 'ms')
		console.log('文件大小:', res.fileSize, 'bytes')
		console.log('录音结束回调被触发的时间:', new Date().toISOString())

		isRecording.value = false
		audioFilePath = res.tempFilePath

		// 若为小程序且有分帧PCM，优先直接拼接发送RAW到云函数
		if ((process.env.UNI_PLATFORM || '') === 'mp-weixin' && wxPcmTotalLen > 0 && wxPcmChunks.length >
			0) {
			try {
				const pcm = new Uint8Array(wxPcmTotalLen)
				let offset = 0
				for (const c of wxPcmChunks) {
					pcm.set(c, offset);
					offset += c.length
				}
				// 清空缓存
				wxPcmChunks = []
				wxPcmTotalLen = 0
				// 直接发送裸PCM（16bit LE，16k，单声道）
				const base64 = uint8ToBase64(pcm)
				await uniCloud.callFunction({
					name: 'speech-recognition',
					data: {
						audioBase64: base64,
						audioFormat: 'raw'
					}
				}).then(result => {
					console.log('uniCloud返回结果(小程序RAW):', result)
					if (result.result.code === 0) {
						voiceResult.value = result.result.data.text || '识别结果为空'
					} else {
						voiceResult.value = `识别失败: ${result.result.message}`
					}
				}).catch(err => {
					console.error('uniCloud调用失败(小程序RAW):', err)
					voiceResult.value = '语音识别服务异常，请重试'
				})
				return
			} catch (e) {
				console.warn('组装小程序PCM失败，回退使用临时文件上传:', e)
			}
		}

		// 验证文件是否存在
		if (res.tempFilePath) {
			console.log('录音文件路径有效，开始上传到uniCloud')
			uploadToUniCloud(audioFilePath)
		} else {
			console.error('录音文件路径无效')
			voiceResult.value = '录音文件生成失败'
		}
	}

	// 模糊层宽度状态
	const layerWidth = ref('100rpx')
	// 是否显示更多内容
	const activeId = ref(null)

	const expand = (id) => {
		safeExecute(() => {
			// 点击按钮时隐藏医疗车覆盖层
			if (overlayVisible.value) {
				overlayVisible.value = false
			}
			// console.log('点击展开')
			// console.log(activeId.value);
			activeId.value = activeId.value === id ? null : id

			// 根据状态设置宽度  如果id等于1，则不扩展宽度
			if (activeId.value !== null && activeId.value !== 1) {
				// 扩展宽度
				layerWidth.value = '380rpx'
				showVisualDiagnosis.value = false;
				showChestExpansion.value = false;
				showWholeLung.value = false;
				showLungLowerBound.value = false;
				showPleuraExpansion.value = false;
				showLungMobility.value = false;
				currentPercussionView.value === VIEW_TYPES.FRONT;

			} else {
				// 恢复原始宽度
				layerWidth.value = '100rpx'
			}

			// 注意：导航按钮不会关闭测量结果气泡
		})
	}
	const showSeat = ref(false)
	const showSeatUp = ref(false)
	const selectedPosition = ref(null)
	const selectedCloth = ref(null)
	// 选择体位/衣服
	const selectPosition = (item) => {
		// 关闭气泡
		if (showMeasurementResult.value) {
			hideBubble();
		}
		console.log('选择体位:', item.text);
		// 判断 item 是否体位
		if (positionList.value.some(pos => pos.id === item.id)) {
			selectedPosition.value = item;
		}
		// 判断 item 是否穿衣条件
		if (clothList.value.some(cloth => cloth.id === item.id)) {
			selectedCloth.value = item;
		}
		// 体位选择：点击"坐位"
		if (item.text === '坐位') {
			bgImage.value = bgImages.wardEmpty;
			showSeat.value = true;
			showSeatUp.value = false;
		} else if (item.text === '暴露上半身') {
			// 衣服选择：点击"暴露上半身"
			showSeat.value = false;
			showSeatUp.value = true;
			// 背景不变
		} else {
			// 其他体位/衣服选择时可按需重置
			// showSeat.value = false;
			// showSeatUp.value = false;
		}
		// 这里可以添加选择体位后的其他逻辑
	}

	// 通用关闭测量结果气泡函数
	const hideBubble = () => {
		// 先播放隐藏动画
		bubbleHiding.value = true;

		// 动画结束后再真正隐藏气泡
		setTimeout(() => {
			showMeasurementResult.value = false;
			bubbleHiding.value = false;
		}, 300); // 与动画时长匹配
	}




	// 处理容器点击事件 - 关闭气泡
	const handleContainerClick = (event) => {
		const target = event.target;

		// 检查点击的元素是否在气泡内或导航按钮内
		let isInMeasurementBubble = false;
		let isInNavigation = false;
		let currentEl = target;

		// 向上遍历DOM树检查元素类型
		while (currentEl && !isInMeasurementBubble && !isInNavigation) {
			if (currentEl.className && typeof currentEl.className === 'string') {
				// 检查是否在测量结果气泡内
				if (currentEl.className.includes('measurement-bubble')) {
					isInMeasurementBubble = true;
				}

				// 检查是否在导航按钮内
				if (currentEl.className.includes('button-item')) {
					isInNavigation = true;
				}
			}
			currentEl = currentEl.parentNode;
		}

		// 处理测量结果气泡
		if (showMeasurementResult.value && !isInMeasurementBubble && !isInNavigation) {
			hideBubble();
		}
	}


	// 添加页面生命周期钩子，确保所有Promise都被正确处理
	// onMounted(() => {
	//   console.log('Ward页面已挂载')
	// })

	const hasThermometer = ref(false);

	onMounted(() => {
		const pos = uni.getStorageSync('thermometerPosition');
		if (pos === 'cart') {
			uni.setStorageSync('thermometerPosition', 'ward');
			hasThermometer.value = true;
		} else if (pos === 'ward') {
			hasThermometer.value = true;
		} else {
			hasThermometer.value = false;
		}
	});


	onShow(() => {
		const pos = uni.getStorageSync('thermometerPosition');
		hasThermometer.value = pos === 'ward';
	});

	// 手腕点击处理
	const handleWristClick = () => {
		console.log(111);
		if (isMeasuring.value || isPulseBreathMeasuring.value) {
			uni.showToast({
				title: '测量中，请稍候...',
				icon: 'none',
				duration: 1500
			})
			return
		}

		// 打开医疗车弹窗
		overlayVisible.value = true
		// 直接开始测量，无需选择时间
		startPulseBreathMeasurement()
	}

	function handlePhyCheck(item) {
		// 体格检查实际逻辑...
		resetAllAreaStates(); // 每次进入视诊时重置
		//隐藏右侧模块
		layerWidth.value = '100rpx' //恢复原始宽度
		activeId.value = null //取消激活状态
		//显示麦克风
		activeId.value = 1
		nextTick(() => {
			setTimeout(() => {

			}, 100)
		})
		//视诊内容
		const diagnosisInputValue = ''
		const visualDiagnosisInput = () => {
			console.log(diagnosisInputValue)
		}
		showVisualDiagnosis.value = true
		// if (item.text === '胸廓前后径比例') {
		// 	showMeasurementResult.value = true;
		// 	measurementResult.value = '胸廓前后径比例：正常（1:2）';
		// 	bubbleHiding.value = false;
		// 	return;
		// }
		// uni.showToast({ title: `进行体格检查：${item.text}`, icon: 'success' })
	}
	// 需要确保visualDiagnosisRibs[0]存在后再初始化
	const taskStatus = ref([])

	watch(() => visualDiagnosisRibs.value[currentViewIndex.value]?.areas, (newAreas) => {
		taskStatus.value = newAreas ? Array(newAreas.length).fill(false) : []
	}, {
		immediate: true
	})
	// 处理不可点击矩形框的点击（画线任务）
	const handleNoClickRect = (areaId, rectIndex) => {
		if (!currentExpectedArea.value) {
			showRectHint.value = true;
			rectHintText.value = '请先说出要标注的位置';
			setTimeout(() => {
				showRectHint.value = false;
			}, 2000);
			return;
		}

		if (areaId !== currentExpectedArea.value) {
			showRectHint.value = true;
			rectHintText.value = '选择错误，请重试';
			setTimeout(() => {
				showRectHint.value = false;
			}, 2000);
			return;
		}
		const currentViewData = visualDiagnosisRibs.value[currentViewIndex.value]
		if (!currentViewData) return

		const area = currentViewData.areas.find(a => a.id === areaId)
		if (!area || !area.drawingTask) return

		// 显示提示信息
		rectHintText.value = `请使用画线工具标注${area.text}`;
		showRectHint.value = true;
		showStartDrawingButton.value = true;
	}

	const showCanvas = ref(false);
	const showDrawingMode = ref(false);
	const isDrawing = ref(false);
	const startPoint = ref(null); // 线条起点
	const currentLine = ref(null)
	const lines = ref([]); // 存储所有线条
	const ctx = ref(null); // 画布上下文
	// 完成任务检查
	// 检查当前视图的非画线任务是否全部完成
	const checkNonDrawingTasksCompleted = () => {
		const nonDrawingTasks = getClickableAreas()
		return nonDrawingTasks.every(area =>
			areaClickStates.value[area.id]?.isCompleted
		)
	}
	// 重绘画布 - 优化垂直线条显示
	const redrawCanvas = () => {
		if (!ctx.value) return;

		ctx.value.clearRect(0, 0, 1000, 1000);

		// 绘制所有已保存的线条
		lines.value.forEach(line => {
			ctx.value.beginPath();
			ctx.value.moveTo(line.startX, line.startY);
			ctx.value.lineTo(line.endX, line.endY);
			ctx.value.setStrokeStyle('#FF0000');
			ctx.value.setLineWidth(2);
			ctx.value.setLineCap('round');
			ctx.value.setLineDash([]);
			ctx.value.stroke();
		});

		// 绘制当前正在绘制的线（预览）
		if (currentLine.value) {
			ctx.value.beginPath();
			ctx.value.moveTo(currentLine.value.startX, currentLine.value.startY);
			ctx.value.lineTo(currentLine.value.endX, currentLine.value.endY);
			ctx.value.setStrokeStyle('#FF000080'); // 半透明预览线
			ctx.value.setLineDash([5, 5]); // 虚线预览
			ctx.value.setLineWidth(2);
			ctx.value.setLineCap('round');
			ctx.value.stroke();
		}

		ctx.value.draw(true);
	};

	// 检查当前视图是否有画线任务
	const hasDrawingTasks = () => {
		return getDrawingAreas().length > 0
	}
	const checkTasksCompletion = () => {
		const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || []
		const completedTasks = currentAreas.filter((_, index) => isTaskCompleted(index)).length
		console.log('检查任务完成情况:', {
			hasDrawingTasks: hasDrawingTasks(),
			nonDrawingTasksCompleted: checkNonDrawingTasksCompleted(),
			currentView: currentViewIndex.value
		});

		console.log('是否显示画线按钮:', showStartDrawingButton.value);
		showStartDrawingButton.value = hasDrawingTasks()
	}

	// 在 completeTask 方法末尾添加检查
	const completeTask = (index) => {
		taskStatus.value[index] = true;
		checkTasksCompletion();
	};

	// 开始画线模式
	const startDrawing = () => {
		showStartDrawingButton.value = false
		showDrawingMode.value = true

		nextTick(() => {
			ctx.value = uni.createCanvasContext('drawingCanvas', this)
			ctx.value.setStrokeStyle('#FF0000')
			ctx.value.setLineWidth(2)
			ctx.value.setLineCap('round')
			redrawCanvas()
		})
	}

	// 绘制直线
	const drawLine = (x1, y1, x2, y2, isPreview = false) => {
		ctx.value.beginPath();
		ctx.value.moveTo(x1, y1);
		ctx.value.lineTo(x2, y2);

		if (isPreview) {
			ctx.value.setStrokeStyle('#FF000080'); // 半透明预览线
			ctx.value.setLineDash([5, 5]); // 虚线预览
		} else {
			ctx.value.setStrokeStyle('#FF0000');
			ctx.value.setLineDash([]);
		}

		ctx.value.stroke();
		ctx.value.draw(true);
	};


	// 清除所有线条
	const clearAllLines = () => {
		lines.value = []
		currentLine.value = null
		if (ctx.value) {
			ctx.value.clearRect(0, 0, 1000, 1000)
			ctx.value.draw()
		}
	}
	const initNoClickRects = () => {
		noClickRects.value = []; // 清空数组
		const areas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || [];

		areas.forEach(area => {
			area.rects?.forEach((rect, rectIndex) => {
				noClickRects.value.push({
					id: area.id,
					rectIndex: rectIndex,
					left: parseInt(rect.left),
					top: parseInt(rect.top),
					width: rect.width,
					height: rect.height,
					completed: false
				});
			});
		});
	};

	// 检查线条是否在矩形框内
	const checkVerticalLineInRect = (line, rect) => {
		// 确保是垂直线 (X坐标相同)
		if (line.startX !== line.endX) return false;

		const lineX = line.startX;
		const lineTop = Math.min(line.startY, line.endY);
		const lineBottom = Math.max(line.startY, line.endY);

		const rectLeft = rect.left;
		const rectRight = rect.left + rect.width;
		const rectTop = rect.top;
		const rectBottom = rect.top + rect.height;

		// 检查垂直线是否穿过矩形框
		return lineX >= rectLeft &&
			lineX <= rectRight &&
			lineTop <= rectBottom &&
			lineBottom >= rectTop;
	};
	// 在handleDrawingEnd方法中添加检测逻辑
	const handleDrawingEnd = (x, y) => {
		if (!isDrawing.value || !startPoint.value) return;

		if (!currentExpectedArea.value) {
			showRectHint.value = true;
			rectHintText.value = '请先说出要标注的位置';
			setTimeout(() => {
				showRectHint.value = false;
			}, 2000);
			return;
		}

		try {
			// 创建垂直的线条 (X坐标相同)
			const newLine = {
				startX: startPoint.value.x,
				startY: startPoint.value.y,
				endX: startPoint.value.x,
				endY: y,
				timestamp: Date.now()
			};

			// 检查线条是否符合要求（最小长度）
			const minLength = 20;
			const lineLength = Math.abs(newLine.endY - newLine.startY);

			if (lineLength < minLength) {
				showRectHint.value = true;
				rectHintText.value = '线条太短，请画更长的垂直线';
				setTimeout(() => {
					showRectHint.value = false;
				}, 1000);
			} else {
				// 检查新线条是否在画线任务的矩形框内
				var drawingAreas = getDrawingAreas();
				var shouldComplete = false;
				var completedAreaId = null;

				drawingAreas.forEach(area => {
					if (area.id !== currentExpectedArea.value) return;

					area.rects.forEach((rect, rectIndex) => {
						const rectBounds = {
							left: parseInt(rect.left),
							top: parseInt(rect.top),
							width: rect.width,
							height: rect.height
						};

						if (checkVerticalLineInRect(newLine, rectBounds)) {
							if (!areaClickStates.value[area.id]) {
								areaClickStates.value[area.id] = {
									lines: [],
									firstLineTime: null,
									isCompleted: false
								};
							}

							const state = areaClickStates.value[area.id];
							if (state.lines.length === 0) {
								state.firstLineTime = newLine.timestamp;
							}

							const alreadyDrawn = state.lines.some(line =>
								line.rectIndex === rectIndex
							);

							if (!alreadyDrawn) {
								state.lines.push({
									...newLine,
									rectIndex: rectIndex
								});
							}

							if (area.requireAll) {
								if (state.lines.length === area.rects.length) {
									const timeElapsed = newLine.timestamp - state
										.firstLineTime;
									if (timeElapsed <= area.timeout) {
										shouldComplete = true;
										completedAreaId = area.id;
									} else {
										showRectHint.value = true;
										rectHintText.value =
											`超时！请在${area.timeout/1000}秒内完成所有线条标注`;
										resetAreaState(area.id);
									}
								}
							} else {
								shouldComplete = true;
								completedAreaId = area.id;
							}
						}
					});
				});

				lines.value.push(newLine);

				if (shouldComplete && completedAreaId) {
					const area = drawingAreas.find(a => a.id === completedAreaId);
					if (area) {
						completeArea(completedAreaId, area);
						currentExpectedArea.value = null; // 重置期望区域
					}
				}
			}

			isDrawing.value = false;
			startPoint.value = null;
			currentLine.value = null;
			redrawCanvas();
		} catch (e) {
			console.error('画线结束错误:', e);
		}
	};

	const resetSingleAreaLines = (areaId) => {
		if (!areaClickStates.value[areaId]) return;

		// 1. 从全局线条数组中移除该任务相关的线条
		const lineTimestamps = areaClickStates.value[areaId].lines.map(l => l.timestamp);
		lines.value = lines.value.filter(line => !lineTimestamps.includes(line.timestamp));

		// 2. 重置该任务状态（保留其他任务状态）
		areaClickStates.value[areaId] = {
			lines: [],
			firstLineTime: Date.now(), // 重置计时器
			isCompleted: false
		};

		// 3. 立即重绘
		nextTick(redrawCanvas);
	};

	// 在onMounted中添加初始化
	onMounted(() => {
		initNoClickRects();
	});
	// 触摸/鼠标事件处理
	const handleDrawingStart = (x, y) => {
		if (!showDrawingMode.value) return;
		try {
			isDrawing.value = true;
			startPoint.value = {
				x,
				y
			};
			currentLine.value = {
				startX: x,
				startY: y,
				endX: x, // 初始时终点X与起点相同，确保垂直
				endY: y
			};
			redrawCanvas();
		} catch (e) {
			console.error('画线开始错误:', e);
		}
	};
	const handleDrawingMove = (x, y) => {
		if (!isDrawing.value || !startPoint.value) return;
		try {
			// 只修改Y坐标，X坐标保持不变，确保线条垂直
			currentLine.value = {
				startX: startPoint.value.x,
				startY: startPoint.value.y,
				endX: startPoint.value.x, // X坐标始终与起点相同
				endY: y // 只更新Y坐标
			};
			redrawCanvas();
		} catch (e) {
			console.error('画线移动错误:', e);
		}
	};

	// 事件处理函数
	const handleTouchStart = (e) => {
		const touch = e.touches[0];
		handleDrawingStart(touch.x, touch.y);
	};

	const handleTouchMove = (e) => {
		const touch = e.touches[0];
		handleDrawingMove(touch.x, touch.y);
	};

	const handleTouchEnd = (e) => {
		if (e.changedTouches.length > 0) {
			const touch = e.changedTouches[0];
			handleDrawingEnd(touch.x, touch.y);
		}
	};

	const handleMouseDown = (e) => {
		handleDrawingStart(e.detail.x, e.detail.y);
	};

	const handleMouseMove = (e) => {
		handleDrawingMove(e.detail.x, e.detail.y);
	};

	const handleMouseUp = (e) => {
		handleDrawingEnd(e.detail.x, e.detail.y);
	};

	// 阻止右键菜单
	const handleContextMenu = (e) => {
		e.preventDefault();
	};
	// 选择答案
	function selectAnswer(optionId) {
		if (showAnswerResult.value) return // 已显示结果，不允许再选择
		userAnswer.value = optionId
		checkAnswer()
	}

	// 检查答案
	function checkAnswer() {
		const currentQuestion = questions.value[currentQuestionIndex.value]
		const selectedOption = currentQuestion.options.find(option => option.id === userAnswer.value)
		isAnswerCorrect.value = selectedOption.correct
		showAnswerResult.value = true

		// 1.5秒后自动跳转到下一题
		setTimeout(() => {
			nextQuestion()
		}, 1500)
	}

	// 下一题
	function nextQuestion() {
		if (currentQuestionIndex.value < questions.value.length - 1) {
			currentQuestionIndex.value++
			resetQuestionState()
		} else {
			// 所有题目完成
			questionCompleted.value = true
		}
	}

	// 重置题目状态
	function resetQuestionState() {
		userAnswer.value = null
		showAnswerResult.value = false
		isAnswerCorrect.value = false
	}

	// 重置所有题目
	function resetAllQuestions() {
		currentQuestionIndex.value = 0
		questionCompleted.value = false
		resetQuestionState()
	}

	//体温测量填空弹窗相关变量
	const showTempQuizDialog = ref(false)
	const tempQuizImage = ref('https://i.postimg.cc/pdC7wptV/image.png') // 可替换为实际图片
	const tempQuizCorrectValue = ref('37.8')
	const tempQuizUserInput = ref('')
	const tempQuizResult = ref('')

	// 体温测量填空题提交校验
	function submitTempQuiz() {
		// 允许小数点后一位误差±0.1
		const correct = parseFloat(tempQuizCorrectValue.value)
		const user = parseFloat(tempQuizUserInput.value)
		if (isNaN(user)) {
			tempQuizResult.value = '请填写体温数值'
			return
		}
		if (Math.abs(correct - user) <= 0.1) {
			tempQuizResult.value = '回答正确'
		} else {
			tempQuizResult.value = '回答错误'
		}
	}
	// 重置所有区域的点击状态
	const resetAllAreaStates = () => {
		areaClickStates.value = {}
		taskStatus.value = Array(
			visualDiagnosisRibs.value[currentViewIndex.value]?.areas?.length || 0
		).fill(false)
	}

	// 完善触诊事件处理
	const palpationThorax = ref({
		[VIEW_TYPES.FRONT]: {
			areas: [{
				id: 1,
				text: "拇指位于剑突",
				rects: [{
					left: '225px',
					top: '120px',
					width: 28,
					height: 28,
					color: 'red'
				}],
				timeout: 3000, // 3秒内完成
			}]
		},
		[VIEW_TYPES.BACK]: {
			areas: [{
				id: 2,
				text: "第10肋骨水平",
				rects: [{
						left: '174px',
						top: '158px',
						width: 40,
						height: 40,
						color: 'blue'
					},
					{
						left: '271px',
						top: '158px',
						width: 40,
						height: 40,
						color: 'blue'
					}
				],
				timeout: 3000, // 3秒内完成
			}]
		},
	})
	const currentThoraxIndex = ref(0)
	const resetChestExpansionState = () => {
		// 设置纯色背景
		showPlainBackground.value = true;
		// 显示胸廓扩张度容器
		showChestExpansion.value = true;
		// 确保右侧导航栏和麦克风可见
		layerWidth.value = '100rpx';
		activeId.value = 1;
		currentThoraxIndex.value = 0; // 重置为正面视图
		resetGame(); // 重置游戏状态
		showBreathingAnimation.value = false; // 隐藏呼吸动图
		showReportResult.value = false; // 隐藏汇报结果提示
		voiceResult.value = ""; // 清空语音结果
	}
	const resetPleuraExpansionState = () => {
		showPlainBackground.value = true;
		showPleuraExpansion.value = true;
		layerWidth.value = '100rpx';
		activeId.value = 1;

		// 重置手部位置
		leftHandPosition.x = 0;
		leftHandPosition.y = 300;
		rightHandPosition.x = 50;
		rightHandPosition.y = 300;

		// 重置状态
		handInTarget.left = false;
		handInTarget.right = false;
		pleuraCompleted.value = false;
		showBreathingPleAnimation.value = false;
		handsPlacedCorrectly.value = false;
		voiceResult.value = "";
		if (breathingAnimationTimeout.value) {
			clearTimeout(breathingAnimationTimeout.value);
			breathingAnimationTimeout.value = null;
		}
	};
	// 初始化触觉语颤状态
	const resetTactileFremitusState = () => {
		showPlainBackground.value = true;
		showTactileFremitus.value = true;
		layerWidth.value = '100rpx';
		activeId.value = 1;

		// 重置状态
		currentTactileViewIndex.value = 0;
		currentTactileGroup.value = 1;
		tactileCompleted.value = false;
		isReversePosition.value = false;
		resetTactileHands();
	};
	// 组件卸载时清理定时器
	onUnmounted(() => {
		if (breathingAnimationTimeout.value) {
			clearTimeout(breathingAnimationTimeout.value);
		}
	});
	// 触觉语颤显示控制
	const showTactileFremitus = ref(false);
	const handlePalpationCheck = (item) => {
		// 关闭所有其他视图
		shouldHideCartIcon.value = true;
		showVisualDiagnosis.value = false;
		showLungLowerBound.value = false;
		showWholeLung.value = false;
		showLungMobility.value = false;
		showDynamicImage.value = false;
		showSeat.value = false;
		showSeatUp.value = false;
		voiceResult.value = "";

		// 如果是胸廓扩张度
		if (item.text === '胸廓扩张度') {
			showPleuraExpansion.value = false;
			resetChestExpansionState()
			return;
		}
		if (item.text === '胸膜摩擦感') {
			showChestExpansion.value = false;
			resetPleuraExpansionState()
			return;
		}
		if (item.text === '触觉语颤') {
			showChestExpansion.value = false;
			showPleuraExpansion.value = false;
			resetTactileFremitusState();
			return;
		}

		// 其他体格检查逻辑...
		layerWidth.value = '100rpx';
		activeId.value = null;
		activeId.value = 1;
	}
	// 图片数据
	const thoraxImage = [{
			type: 'front',
			url: thoraxImages.front, // 正面图路径
			title: '胸廓正面'
		},
		{
			type: 'back',
			url: thoraxImages.back, // 背面图路径
			title: '胸廓背面'
		}
	]

	const chestGame = reactive({
		targetPoints: computed(() => {
			// 根据当前视图返回不同的目标点
			return currentThoraxIndex.value === 0 ? [{
					areaId: 1,
					rectIndex: 0
				}, // 正面图的目标点
			] : [{
					areaId: 2,
					rectIndex: 0
				}, // 背面图的目标点（第10肋骨水平的两个点）
				{
					areaId: 2,
					rectIndex: 1
				}
			];
		}),
		clickedPoints: [], // 用户已点击的点记录
		startTime: null, // 开始计时的时间戳
		isCompleted: false // 是否已完成
	});
	// 检查是否是目标点
	const isTargetRect = (areaId, rectIndex) => {
		return chestGame.targetPoints.some(p =>
			p.areaId === areaId && p.rectIndex === rectIndex
		)
	}

	// 检查点是否已被点击
	const isPointClicked = (areaId, rectIndex) => {
		return chestGame.clickedPoints.some(p =>
			p.areaId === areaId && p.rectIndex === rectIndex
		)
	}
	// 获取当前视图的标记框数据
	const getCurrentRects = () => {
		const viewType = currentThoraxIndex.value === 0 ? VIEW_TYPES.FRONT : VIEW_TYPES.BACK
		const areas = palpationThorax.value[viewType]?.areas || []

		return areas.flatMap(area => {
			return area.rects.map((rect, rectIndex) => {
				// 检查是否是目标点（区域5的所有点或区域6的所有点）
				const isTarget = chestGame.targetPoints.some(
					point => point.areaId === area.id && point.rectIndex === rectIndex
				)

				return {
					...rect,
					areaId: area.id,
					rectIndex: rectIndex, // 添加rectIndex
					isTarget: isTarget // 精确标记每个点是否是目标点
				}
			})
		})
	}
	const showReportResult = ref(false); // 是否显示汇报结果阶段
	// 切换视图
	const switchView = () => {
		if (showReportResult.value) return;
		currentThoraxIndex.value = currentThoraxIndex.value === 0 ? 1 : 0;
		resetGame();
		showBreathingAnimation.value = false;
		voiceResult.value = "";
	};
	const handleChestClick = (rect, areaId, rectIndex) => {
		if (!warmHandsCompleted.value) {
			handleChestClickAnywhere();
			return;
		}
		if (chestGame.isCompleted) return;

		// 第一次点击开始计时
		if (!chestGame.startTime) {
			chestGame.startTime = Date.now();
		}

		// 检查是否超时（5秒内）
		if (Date.now() - chestGame.startTime > 5000) {
			resetGame();
			uni.showToast({
				title: '操作超时，请重试',
				icon: 'none'
			});
			return;
		}

		// 检查是否点击了目标区域
		const isTarget = isTargetRect(areaId, rectIndex);

		if (isTarget) {
			const clickedPoint = {
				areaId,
				rectIndex
			};

			if (!chestGame.clickedPoints.some(p =>
					p.areaId === clickedPoint.areaId &&
					p.rectIndex === clickedPoint.rectIndex
				)) {
				chestGame.clickedPoints.push(clickedPoint);
			}

			// 检查是否完成
			checkCompletion();
		} else {
			uni.showToast({
				title: '选择错误，请点击正确的标记位置',
				icon: 'none'
			});
		}
	};
	const checkCompletion = () => {
		// 检查是否点击了所有目标点（顺序无关）
		const isCorrect = chestGame.targetPoints.every(target =>
			chestGame.clickedPoints.some(clicked =>
				clicked.areaId === target.areaId &&
				clicked.rectIndex === target.rectIndex
			)
		);

		// 检查是否点击了多余的点
		const hasExtraPoints = chestGame.clickedPoints.some(clicked =>
			!chestGame.targetPoints.some(target =>
				target.areaId === clicked.areaId &&
				target.rectIndex === clicked.rectIndex
			)
		);

		if (isCorrect && !hasExtraPoints) {
			chestGame.isCompleted = true;
			uni.showToast({
				title: '选择正确！',
				icon: 'success'
			});
		} else if (hasExtraPoints) {
			resetGame();
			uni.showToast({
				title: '选择错误，请重试',
				icon: 'none'
			});
		}
	};

	const resetGame = () => {
		chestGame.clickedPoints = [];
		chestGame.startTime = null;
		chestGame.isCompleted = false;
		showBreathingAnimation.value = false;
	};
	const handleImageClick = () => {
		if (!warmHandsCompleted.value) {
			uni.showToast({
				title: '请完成暖手操作',
				icon: 'none',
				duration: 2000
			});
		}
	};

	const handleChestClickAnywhere = () => {
		if (!warmHandsCompleted.value) {
			uni.showToast({
				title: '请完成暖手操作',
				icon: 'none',
				duration: 2000
			});
			return;
		}
		if (!chestGame.isCompleted) {
			uni.showToast({
				title: '选择错误，请点击正确的标记位置',
				icon: 'none'
			});
		}
	};
	const showBreathingAnimation = ref(false);
	const breathingImages = ref({
		[VIEW_TYPES.FRONT]: visualDiagnosisImages.frontVideo,
		[VIEW_TYPES.BACK]: visualDiagnosisImages.behindVideo
	});
	// 获取当前呼吸动图
	const getCurrentBreathingImage = () => {
		return breathingImages.value[currentThoraxIndex.value === 0 ? VIEW_TYPES.FRONT : VIEW_TYPES.BACK];
	};
	// 开始暖手动画
	const startWarmHandsAnimation = () => {
		showWarmHandsAnimation.value = true;

		// 4秒后动画结束
		setTimeout(() => {
			showWarmHandsAnimation.value = false;
			warmHandsCompleted.value = true;
			uni.showToast({
				title: '暖手完成',
				icon: 'success'
			});
		}, 1500);
	};

	// 进入汇报结果阶段
	const showReportResultStage = () => {
		showReportResult.value = true;
		voiceResult.value = "";
	};
	//胸膜摩擦感相关状态
	const showBreathingPleAnimation = ref(false);
	const handsPlacedCorrectly = ref(false);
	const breathingAnimationTimeout = ref(null);
	const leftHandPosition = reactive({
		x: 0,
		y: 300
	});
	const rightHandPosition = reactive({
		x: 50,
		y: 300
	});
	const pleuraTargetRects = ref([{
			id: 'left',
			left: '132px',
			top: '137px',
			width: 45,
			height: 50,
			color: 'blue'
		},
		{
			id: 'right',
			left: '247px',
			top: '137px',
			width: 45,
			height: 50,
			color: 'blue'
		}
	]);

	const handInTarget = reactive({
		left: false,
		right: false
	});
	const pleuraCompleted = ref(false);

	const checkHandInTarget = (hand, x, y) => {
		const target = pleuraTargetRects.value.find(r => r.id === hand);
		if (!target) return;

		const tolerance = 25; // 容错范围，单位px
		const targetLeft = parseInt(target.left) - tolerance;
		const targetTop = parseInt(target.top) - tolerance;
		const targetRight = parseInt(target.left) + target.width + tolerance;
		const targetBottom = parseInt(target.top) + target.height + tolerance;

		// 添加调试信息
		// console.log(`Hand ${hand} position: x=${x}, y=${y}`);
		// console.log(
		//   `Target ${hand} area (with tolerance): left=${targetLeft}, top=${targetTop}, right=${targetRight}, bottom=${targetBottom}`
		// );

		// 检查是否在容错范围内
		const inTarget = x >= targetLeft && x <= targetRight &&
			y >= targetTop && y <= targetBottom;

		handInTarget[hand] = inTarget;

		// 检查是否两只手都放对了
		if (handInTarget.left && handInTarget.right) {
			setTimeout(() => {
				handsPlacedCorrectly.value = true;
				uni.showToast({
					title: '双手放置正确！',
					icon: 'success'
				});
			}, 1000)
		}
	};
	const onHandDrag = (e, hand) => {
		const {
			x,
			y
		} = e.detail;
		if (hand === 'left') {
			leftHandPosition.x = x;
			leftHandPosition.y = y;
		} else {
			rightHandPosition.x = x;
			rightHandPosition.y = y;
		}
		checkHandInTarget(hand, x, y);
	}
	// 呼吸动画流程
	const startBreathingAnimation = () => {
		// 隐藏静态图，显示动图
		showBreathingPleAnimation.value = true;


		// 4秒后结束动画
		breathingAnimationTimeout.value = setTimeout(() => {
			endBreathingAnimation();
		}, 10000);
	};
	const endBreathingAnimation = () => {
		// 隐藏动图，显示结果
		showBreathingPleAnimation.value = false;
		pleuraCompleted.value = true;

		// 显示结果
		setTimeout(() => {
			uni.showToast({
				title: '胸膜摩擦感完成',
				icon: 'success',
				duration: 2000
			});
		}, 1000)

	};
	// 添加叩诊相关变量
	const percussionImageStyle = ref({});
	const showWholeLung = ref(false); //全肺叩诊
	const showLungLowerBound = ref(false); //肺下界叩诊
	const showLungMobility = ref(false); //肺下界移动度
	const showPercussionImages = ref(false);
	// 尺子刻度
	const rulerMarks = computed(() => {
		return [0, 1, 2, 3, 4];
	});
	const percussionImages = ref([{
			url: visualDiagnosisImages.percussionOne,
			correct: false
		},
		{
			url: visualDiagnosisImages.percussionTwo,
			correct: false
		},
		{
			url: visualDiagnosisImages.percussionThree,
			correct: true
		}
	]);

	const currentPercussionHint = ref('');
	const handlePercussionCheck = (item) => {
		// 关闭所有其他视图
		shouldHideCartIcon.value = true;
		showVisualDiagnosis.value = false;
		showChestExpansion.value = false;
		showPleuraExpansion.value = false;
		showLungMobility.value = false;
		showDynamicImage.value = false;
		showSeat.value = false;
		showSeatUp.value = false;
		voiceResult.value = "";
		// 其他体格检查逻辑...
		layerWidth.value = '100rpx';
		activeId.value = null;
		// 设置纯色背景
		showPlainBackground.value = true;
		// 如果是全肺叩诊
		if (item.text === '全肺叩诊') {
			showLungLowerBound.value = false;
			activeId.value = null //取消激活状态
			//显示麦克风
			activeId.value = 1;
			showWholeLung.value = true;
			showPercussionImages.value = true;
			// 重置状态
			currentViewIndex.value = VIEW_TYPES.FRONT;
			currentPercussionHint.value = '请按顺序叩诊各个点位';
		}
		if (item.text === '肺下界叩诊') {
			showWholeLung.value = false;
			activeId.value = null //取消激活状态
			showLungLowerBound.value = true;
			// 重置状态
			currentViewIndex.value = VIEW_TYPES.FRONT;
			currentPercussionHint.value = '请按顺序叩诊各个点位';
		}
		if (item.text === '肺下界移动度') {
			showWholeLung.value = false;
			showLungLowerBound.value = false;
			activeId.value = null;
			showLungMobility.value = true;

			// 重置移动度状态
			lungMobilityState.value = {
				phase: 1,
				currentArea: 1,
				clickedIds: [],
				completedAreas: [],
				markedPoints: []
			};
		}

	}
	// 添加叩诊图片点击处理
	const handlePercussionImageClick = (index) => {
		if (index === 2) { // 第三张图片是正确的
			uni.showToast({
				title: '选择正确！',
				icon: 'success'
			});
		} else {
			uni.showToast({
				title: '选择错误',
				icon: 'error'
			});
		}

		// 1秒后隐藏所有图片
		setTimeout(() => {
			showPercussionImages.value = false;
			showPercussionResult.value = true;
		}, 1000);
	}
	// 叩诊图片数据
	const percussionImagesData = [{
			type: 'front',
			url: visualDiagnosisImages.wholeLungFront, // 正面图路径
			title: '胸廓正面'
		},
		{
			type: 'side-left',
			url: visualDiagnosisImages.wholeLungSideLeft, // 左侧面图路径
			title: '胸廓左侧面'
		},
		{
			type: 'side-right',
			url: visualDiagnosisImages.wholeLungSideRight, // 右侧面图路径
			title: '胸廓右侧面'
		},
		{
			type: 'back',
			url: visualDiagnosisImages.wholeLungBack, // 背面图路径
			title: '胸廓背面'
		}
	];
	const currentPercussionView = ref(VIEW_TYPES.FRONT);
	const getCurrentPercussionImage = () => {
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			// 侧面视图根据当前叩诊侧返回对应图片
			return currentPercussionSide.value === 'left' ?
				percussionImagesData[1].url // 左侧图片
				:
				percussionImagesData[2].url; // 右侧图片
		}

		// 正面和背面视图
		return currentPercussionView.value === VIEW_TYPES.FRONT ?
			percussionImagesData[0].url // 正面图片
			:
			percussionImagesData[3].url; // 背面图片
	}
	const showPercussionResult = ref(false)
	//正面叩诊点
	const percussionPointsFront = ref({
		[VIEW_TYPES.FRONT]: {
			areas: [{
					id: 1,
					text: "第一肋间",
					rects: [{
							id: 1,
							left: '168px',
							top: '58px',
							width: 70,
							height: 15,
							color: 'red',
							type: '过清音'
						},
						{
							id: 2,
							left: '89px',
							top: '58px',
							width: 75,
							height: 15,
							color: 'red',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 2,
					text: "第二肋间",
					rects: [{
							id: 3,
							left: '89px',
							top: '76px',
							width: 75,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 4,
							left: '178px',
							top: '76px',
							width: 65,
							height: 18,
							color: 'red',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 3,
					text: "第三肋间",
					rects: [{
							id: 5,
							left: '228px',
							top: '97px',
							width: 30,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 6,
							left: '195px',
							top: '97px',
							width: 30,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 7,
							left: '132px',
							top: '97px',
							width: 35,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 8,
							left: '89px',
							top: '98px',
							width: 40,
							height: 18,
							color: 'red',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 4,
					text: "第四肋间",
					rects: [{
							id: 9,
							left: '89px',
							top: '120px',
							width: 40,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 10,
							left: '132px',
							top: '120px',
							width: 35,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 11,
							left: '191px',
							top: '119px',
							width: 35,
							height: 18,
							color: 'red',
							type: '浊音'
						},
						{
							id: 12,
							left: '228px',
							top: '119px',
							width: 35,
							height: 18,
							color: 'red',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 5,
					text: "第五肋间",
					rects: [{
							id: 13,
							left: '228px',
							top: '151px',
							width: 35,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 14,
							left: '190px',
							top: '151px',
							width: 35,
							height: 18,
							color: 'red',
							type: '实音'
						},
						{
							id: 15,
							left: '132px',
							top: '151px',
							width: 35,
							height: 18,
							color: 'red',
							type: '过清音'
						},
						{
							id: 16,
							left: '94px',
							top: '151px',
							width: 35,
							height: 18,
							color: 'red',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 6,
					text: "第六肋间",
					rects: [{
							id: 17,
							left: '107px',
							top: '184px',
							width: 35,
							height: 18,
							color: 'red',
							type: '浊音'
						},
						{
							id: 18,
							left: '209px',
							top: '184px',
							width: 35,
							height: 18,
							color: 'red',
							type: '鼓音'
						}
					],
					timeout: 3000
				},
				{
					id: 7,
					text: "第七肋间",
					rects: [{
							id: 19,
							left: '211px',
							top: '205px',
							width: 35,
							height: 18,
							color: 'red',
							type: '鼓音'
						},
						{
							id: 20,
							left: '111px',
							top: '205px',
							width: 35,
							height: 18,
							color: 'red',
							type: '实音'
						}
					],
					timeout: 3000
				}
			]
		},

	});
	//侧面叩诊点
	const percussionPointsSide = ref({
		[VIEW_TYPES.SIDE]: {
			areas: [{
					id: 1,
					text: '腋窝',
					sides: {
						left: [{
							id: 1,
							left: '167px',
							top: '62px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 2,
							left: '167px',
							top: '62px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '过清音'
						}],

					},
					timeout: 3000
				},
				{
					id: 2,
					text: '第四肋间隙',
					sides: {
						left: [{
							id: 3,
							left: '167px',
							top: '95px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 4,
							left: '167px',
							top: '95px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '过清音'
						}]

					},
					timeout: 3000
				},
				{
					id: 3,
					text: '第五肋间隙',
					sides: {
						left: [{
								id: 5,
								left: '143px',
								top: '133px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 6,
								left: '189px',
								top: '133px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						],
						right: [{
								id: 7,
								left: '143px',
								top: '133px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 8,
								left: '189px',
								top: '133px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						]
					},
					timeout: 3000
				},
				{
					id: 4,
					text: '第六肋间隙',
					sides: {
						left: [{
								id: 9,
								left: '143px',
								top: '162px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 10,
								left: '189px',
								top: '162px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						],
						right: [{
								id: 11,
								left: '143px',
								top: '162px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 12,
								left: '189px',
								top: '162px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						]
					},
					timeout: 3000
				},
				{
					id: 5,
					text: '第七肋间隙',
					sides: {
						left: [{
								id: 13,
								left: '143px',
								top: '191px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 14,
								left: '189px',
								top: '191px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						],
						right: [{
								id: 15,
								left: '143px',
								top: '191px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 16,
								left: '189px',
								top: '191px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						]
					},
					timeout: 3000
				},
				{
					id: 6,
					text: '第八肋间隙',
					sides: {
						left: [{
								id: 17,
								left: '143px',
								top: '220px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 18,
								left: '189px',
								top: '220px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						],
						right: [{
								id: 19,
								left: '143px',
								top: '220px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							},
							{
								id: 20,
								left: '189px',
								top: '220px',
								width: 40,
								height: 18,
								color: 'blue',
								type: '过清音'
							}
						]
					},
					timeout: 3000
				},
			]
		}
	});
	//背面叩诊点
	const percussionPointsBack = ref({
		[VIEW_TYPES.BACK]: {
			areas: [{
					id: 1,
					text: "第二肋间隙",
					rects: [{
							id: 1,
							left: '136px',
							top: '79px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 2,
							left: '187px',
							top: '79px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 2,
					text: "第三肋间隙",
					rects: [{
							id: 3,
							left: '136px',
							top: '95px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 4,
							left: '187px',
							top: '95px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 3,
					text: "第四肋间隙",
					rects: [{
							id: 5,
							left: '136px',
							top: '111px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 6,
							left: '187px',
							top: '111px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 4,
					text: "第五肋间隙",
					rects: [{
							id: 7,
							left: '136px',
							top: '127px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 8,
							left: '187px',
							top: '127px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 5,
					text: "第六肋间隙",
					rects: [{
							id: 9,
							left: '136px',
							top: '142px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 10,
							left: '187px',
							top: '142px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 6,
					text: "第七肋间隙",
					rects: [{
							id: 11,
							left: '136px',
							top: '158px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 12,
							left: '187px',
							top: '158px',
							width: 30,
							height: 12,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 7,
					text: "第八肋间隙",
					rects: [{
							id: 13,
							left: '234px',
							top: '182px',
							width: 25,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 14,
							left: '205px',
							top: '174px',
							width: 25,
							height: 12,
							color: 'green',
							type: '过清音'
						},
						{
							id: 15,
							left: '121px',
							top: '174px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 16,
							left: '92px',
							top: '182px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 8,
					text: "第九肋间隙",
					rects: [{
							id: 17,
							left: '92px',
							top: '212px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 18,
							left: '121px',
							top: '202px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 19,
							left: '205px',
							top: '202px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 20,
							left: '234px',
							top: '212px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				},
				{
					id: 9,
					text: "第十肋间隙",
					rects: [{
							id: 21,
							left: '234px',
							top: '238px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 22,
							left: '205px',
							top: '229px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 23,
							left: '121px',
							top: '229px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						},
						{
							id: 24,
							left: '92px',
							top: '238px',
							width: 25,
							height: 15,
							color: 'green',
							type: '过清音'
						}
					],
					timeout: 3000
				}
			],
		}
	});

	// 定义每个肋间的有效点击顺序
	const frontValidSequences = {
		1: [
			[1, 2],
			[2, 1]
		], // 第一肋间
		2: [
			[3, 4],
			[4, 3]
		], // 第二肋间
		3: [ // 第三肋间
			[5, 6, 7, 8],
			[8, 7, 6, 5],
			[5, 8, 6, 7],
			[8, 5, 7, 6],
			[5, 6, 8, 7],
			[8, 7, 5, 6],
			[8, 5, 6, 7],
			[5, 8, 7, 6]
		],
		4: [ // 第四肋间
			[9, 10, 11, 12],
			[12, 11, 10, 9],
			[9, 10, 12, 11],
			[12, 11, 9, 10],
			[12, 9, 11, 10],
			[12, 9, 10, 11],
			[9, 12, 10, 11],
			[9, 12, 11, 10]
		],
		5: [ // 第五肋间
			[16, 15, 14, 13],
			[13, 14, 15, 16],
			[13, 16, 14, 15],
			[16, 13, 15, 14],
			[13, 14, 16, 15],
			[16, 15, 13, 14],
			[16, 13, 14, 15],
			[13, 16, 15, 14]
		],
		6: [
			[17, 18],
			[18, 17]
		], // 第六肋间
		7: [
			[19, 20],
			[20, 19]
		] // 第七肋间
	};
	// 为侧面叩诊定义专门的顺序规则
	const sideValidSequences = {
		1: [ // 腋窝 - 只有1个点，只需点击即可
			[1],
			[2] // 左侧点1，右侧点2
		],
		2: [ // 第4肋间隙 - 只有1个点
			[3],
			[4] // 左侧点3，右侧点4
		],
		3: [ // 第5肋间隙 - 有2个点，同一侧内顺序任意
			// 左侧
			[5, 6],
			[6, 5],
			// 右侧  
			[7, 8],
			[8, 7]
		],
		4: [ // 第6肋间隙 - 有2个点
			// 左侧
			[9, 10],
			[10, 9],
			// 右侧
			[11, 12],
			[12, 11]
		],
		5: [ // 第7肋间隙 - 有2个点
			// 左侧
			[13, 14],
			[14, 13],
			// 右侧
			[15, 16],
			[16, 15]
		],
		6: [ // 第8肋间隙 - 有2个点
			// 左侧
			[17, 18],
			[18, 17],
			// 右侧
			[19, 20],
			[20, 19]
		]
	};
	// 背面叩诊顺序规则
	const backValidSequences = {
		1: [ // 第2肋间隙 - 2个点，任意顺序
			[1, 2],
			[2, 1]
		],
		2: [ // 第3肋间隙 - 2个点，任意顺序
			[3, 4],
			[4, 3]
		],
		3: [ // 第4肋间隙 - 2个点，任意顺序
			[5, 6],
			[6, 5]
		],
		4: [ // 第5肋间隙 - 2个点，任意顺序
			[7, 8],
			[8, 7]
		],
		5: [ // 第6肋间隙 - 2个点，任意顺序
			[9, 10],
			[10, 9]
		],
		6: [ // 第7肋间隙 - 2个点，任意顺序
			[11, 12],
			[12, 11]
		],
		7: [ // 第8肋间隙 - 4个点，多种顺序组合
			[13, 14, 15, 16],
			[16, 15, 14, 13],
			[13, 16, 14, 15],
			[16, 13, 15, 14],
			[13, 14, 16, 15],
			[16, 15, 13, 14],
			[16, 13, 14, 15],
			[13, 16, 15, 14]
		],
		8: [ // 第9肋间隙 - 4个点，多种顺序组合
			[17, 18, 19, 20],
			[20, 19, 18, 17],
			[20, 19, 17, 18],
			[17, 20, 18, 19],
			[17, 18, 20, 19],
			[17, 20, 19, 18],
			[20, 17, 18, 19],
			[20, 17, 19, 18]
		],
		9: [ // 第10肋间隙 - 4个点，多种顺序组合
			[21, 22, 23, 24],
			[24, 23, 22, 21],
			[24, 23, 21, 22],
			[21, 22, 24, 23],
			[24, 21, 23, 22],
			[24, 21, 22, 23],
			[21, 24, 23, 22],
			[21, 24, 22, 23]
		]
	};
	const lowerBoundPointsFront = ref({
		[VIEW_TYPES.FRONT]: {
			areas: [{
					id: 1,
					text: "第二肋间",
					rects: [{
						id: 1,
						left: '100px',
						top: '74px',
						width: 45,
						height: 20,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 2,
					text: "第三肋间",
					rects: [{
						id: 2,
						left: '100px',
						top: '102px',
						width: 45,
						height: 20,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 3,
					text: "第四肋间",
					rects: [{
						id: 3,
						left: '100px',
						top: '130px',
						width: 45,
						height: 20,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 4,
					text: "第五肋间",
					rects: [{
						id: 4,
						left: '100px',
						top: '158px',
						width: 45,
						height: 20,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 5,
					text: "第六肋间",
					rects: [{
						id: 5,
						left: '100px',
						top: '186px',
						width: 45,
						height: 20,
						color: 'red',
						type: '浊音'
					}],
					timeout: 3000
				},
				{
					id: 6,
					text: "第七肋间",
					rects: [{
						id: 6,
						left: '100px',
						top: '214px',
						width: 45,
						height: 20,
						color: 'red',
						type: '实音'
					}],
					timeout: 3000
				}
			]
		}
	});

	// 肺下界侧面叩诊点
	const lowerBoundPointsSide = ref({
		[VIEW_TYPES.SIDE]: {
			areas: [{
					id: 1,
					text: '腋窝',
					sides: {
						left: [{
							id: 1,
							left: '167px',
							top: '62px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 2,
							left: '167px',
							top: '62px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],

					},
					timeout: 3000
				},
				{
					id: 2,
					text: '第四肋间隙',
					sides: {
						left: [{
							id: 3,
							left: '167px',
							top: '90px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 4,
							left: '167px',
							top: '90px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}]

					},
					timeout: 3000
				},
				{
					id: 3,
					text: '第五肋间隙',
					sides: {
						left: [{
							id: 5,
							left: '167px',
							top: '121px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 6,
							left: '167px',
							top: '121px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}]
					},
					timeout: 3000
				},
				{
					id: 4,
					text: '第六肋间隙',
					sides: {
						left: [{
							id: 7,
							left: '167px',
							top: '152px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 8,
							left: '167px',
							top: '152px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}]
					},
					timeout: 3000
				},
				{
					id: 5,
					text: '第七肋间隙',
					sides: {
						left: [{
							id: 9,
							left: '167px',
							top: '183px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 10,
							left: '167px',
							top: '183px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}]
					},
					timeout: 3000
				},
				{
					id: 6,
					text: '第八肋间隙',
					sides: {
						left: [{
							id: 11,
							left: '167px',
							top: '214px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}],
						right: [{
							id: 12,
							left: '167px',
							top: '214px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '过清音'
						}]
					},
					timeout: 3000
				},
				{
					id: 7,
					text: '第九肋间隙',
					sides: {
						left: [{
							id: 13,
							left: '167px',
							top: '245px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '浊音'
						}],
						right: [{
							id: 14,
							left: '167px',
							top: '245px',
							width: 45,
							height: 18,
							color: 'blue',
							type: '浊音'
						}]
					},
					timeout: 3000
				},
			]
		}
	});

	const lowerBoundPointsBack = ref({
		[VIEW_TYPES.BACK]: {
			areas: [{
				id: 1,
				text: "第七至十一肋间隙",
				rects: [{
						id: 1,
						left: '110px',
						top: '155px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'left'
					},
					{
						id: 2,
						left: '197px',
						top: '155px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'right'
					},
					{
						id: 3,
						left: '110px',
						top: '178px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'left'
					},
					{
						id: 4,
						left: '197px',
						top: '178px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'right'
					},
					{
						id: 5,
						left: '110px',
						top: '201px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'left'
					},
					{
						id: 6,
						left: '197px',
						top: '201px',
						width: 35,
						height: 22,
						color: 'green',
						type: '过清音',
						side: 'right'
					},
					{
						id: 7,
						left: '110px',
						top: '223px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'left'
					},
					{
						id: 8,
						left: '197px',
						top: '223px',
						width: 35,
						height: 20,
						color: 'green',
						type: '过清音',
						side: 'right'
					},
					{
						id: 9,
						left: '110px',
						top: '246px',
						width: 35,
						height: 20,
						color: 'green',
						type: '浊音',
						side: 'left'
					},
					{
						id: 10,
						left: '197px',
						top: '246px',
						width: 35,
						height: 20,
						color: 'green',
						type: '浊音',
						side: 'right'
					}
				],
				timeout: 3000,
				requireAll: true
			}],
		}
	});

	// 修改肺下界背面叩诊顺序规则
	const lowerBoundBackValidSequences = {
		1: [ // 先左侧所有点，再右侧所有点
			[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]
		]
	};
	// 肺下界叩诊状态
	const lowerBoundProgress = ref({
		currentArea: 1,
		clickedIds: [],
		completedAreas: [],
		currentSide: 'left' // 当前叩诊侧：'left' 或 'right'
	});

	// 获取当前肺下界叩诊图片
	const getCurrentLowerBoundImage = () => {
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			// 侧面视图根据当前叩诊侧返回对应图片
			return lowerBoundProgress.value.currentSide === 'left' ?
				percussionImagesData[1].url // 左侧图片
				:
				percussionImagesData[2].url; // 右侧图片
		}

		// 正面和背面视图
		return currentPercussionView.value === VIEW_TYPES.FRONT ?
			percussionImagesData[0].url // 正面图片
			:
			percussionImagesData[3].url; // 背面图片
	};
	const percussionProgress = ref({
		currentArea: 1, //当前点击区域
		clickedIds: [], //当前肋间已点击的rect.id
		completedAreas: [], //已完成的所有肋间
		currentSide: 'left',
		completedSidesPerArea: {}
	})
	const checkSequence = (areaId, clickedIds) => {
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			// 侧面视图：检查是否完成当前侧的所有点
			const sequences = sideValidSequences[areaId];
			if (!sequences) return true;

			return sequences.some(seq => JSON.stringify(seq) === JSON.stringify(clickedIds));
		} else if (currentPercussionView.value === VIEW_TYPES.BACK) {
			// 背面视图：使用背面专用规则
			const sequences = backValidSequences[areaId];
			if (!sequences) return true;

			return sequences.some(seq => JSON.stringify(seq) === JSON.stringify(clickedIds));
		} else {
			// 正面和背面视图使用原有逻辑
			const sequences = frontValidSequences[areaId];
			return sequences.some(seq => JSON.stringify(seq) === JSON.stringify(clickedIds));
		}
	};
	const resetCurrentArea = () => {
		percussionProgress.value.clickedIds = [];
		uni.showModal({
			title: "叩诊顺序错误，请重新叩诊当前肋间",
			showCancel: false
		});
	}
	const isPercussionActive = ref(false)
	const percussionHandVisible = ref(false);
	const percussionHandPosition = reactive({
		left: '0px',
		top: '0px'
	});
	const percussionHandTimer = ref(null);
	// 添加当前叩诊侧状态
	const currentPercussionSide = ref('left');

	// 获取当前视图的叩诊区域
	const getCurrentPercussionAreas = () => {
		// 根据当前视图返回不同的叩诊点数据
		switch (currentPercussionView.value) {
			case VIEW_TYPES.FRONT:
				return percussionPointsFront.value[currentPercussionView.value]?.areas || [];
			case VIEW_TYPES.SIDE:
				// 对于侧面视图，根据当前叩诊侧返回对应数据
				const sideAreas = percussionPointsSide.value[currentPercussionView.value]?.areas || [];
				return sideAreas.map(area => {
					const sideRects = currentPercussionSide.value === 'left' ?
						area.sides.left : area.sides.right;
					return {
						id: area.id,
						text: area.text,
						rects: sideRects,
						timeout: area.timeout
					};
				});
			case VIEW_TYPES.BACK:
				return percussionPointsBack.value[currentPercussionView.value]?.areas || [];
			default:
				return [];
		}
	};
	const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms))
	const switchToNextPercussionView = () => {
		if (currentPercussionView.value === VIEW_TYPES.FRONT) {
			// 从正面切换到侧面（左侧）
			currentPercussionView.value = VIEW_TYPES.SIDE;
			currentPercussionSide.value = 'left'; // 重置为左侧开始
			resetPercussionProgress();
			uni.showToast({
				title: '切换到左侧面视图，请开始叩诊',
				icon: 'success'
			});
		} else if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			currentPercussionView.value = VIEW_TYPES.BACK;
			percussionImageStyle.value = {
				width: '90%'
			};
			resetPercussionProgress();
			uni.showToast({
				title: '侧面叩诊完成，切换到背面视图',
				icon: 'success'
			});
		} else {
			// 完成所有叩诊
			uni.showToast({
				title: '全肺叩诊完成！',
				icon: 'none',
				duration: 2000
			});
		}
	};

	// 处理叩诊点点击
	const handlePercussionPointClick = async (areaId, rectIndex, rectId) => {
		// 获取当前肋间数据
		const area = getCurrentPercussionAreas().find(a => a.id === areaId);
		if (!area) return;

		// 检查是否应该点击当前肋间
		if (areaId !== percussionProgress.value.currentArea) {
			uni.showModal({
				title: '顺序错误',
				content: '请按顺序从当前肋间开始叩诊',
				showCancel: false
			});
			return;
		}

		// 检查是否已经点击过这个点
		if (percussionProgress.value.clickedIds.includes(rectId)) {
			return;
		}

		const rect = area.rects.find(r => r.id === rectId);
		if (!rect) return;

		// 临时保存当前点击序列
		const tempSequence = [...percussionProgress.value.clickedIds, rectId];

		// 检查当前点击是否有效
		const isValidClick = checkPartialSequence(areaId, tempSequence);
		if (!isValidClick) {
			resetCurrentArea();
			return;
		}

		// 有效点击处理
		uni.showToast({
			title: `${rect.type}`,
			icon: 'none',
			duration: 1200
		});
		isPercussionActive.value = true;
		percussionProgress.value.clickedIds.push(rectId);

		// 播放音频和显示手部动画（保持不变）
		playPercussionSound(rect.type);
		percussionHandPosition.left = rect.left;
		percussionHandPosition.top = `${parseInt(rect.top) - 20}px`;
		percussionHandVisible.value = true;

		// 计算当前区域应有点数
		let expectedLength = currentPercussionView.value === VIEW_TYPES.SIDE ?
			(currentPercussionSide.value === 'left' ?
				percussionPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === areaId)?.sides.left.length :
				percussionPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === areaId)?.sides.right.length) :
			area.rects.length;

		// 核心逻辑：处理侧面视图的交替叩诊
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			if (percussionProgress.value.clickedIds.length === expectedLength) {
				// 当前侧完成
				await delay(1200);
				isPercussionActive.value = false;
				percussionHandVisible.value = false;

				// 记录当前肋间的完成侧
				percussionProgress.value.completedSidesPerArea[areaId] = {
					...percussionProgress.value.completedSidesPerArea[areaId],
					[currentPercussionSide.value]: true
				};

				// 检查是否双侧都已完成
				const bothSidesComplete = percussionProgress.value.completedSidesPerArea[areaId]?.left &&
					percussionProgress.value.completedSidesPerArea[areaId]?.right;

				if (currentPercussionSide.value === 'left') {
					// 左侧完成，切换到同一肋间的右侧
					currentPercussionSide.value = 'right';
					percussionProgress.value.clickedIds = [];
					uni.showModal({
						title: '左侧叩诊完成，请你转身',
						showCancel: false
					});
				} else {
					// 右侧完成
					if (bothSidesComplete) {
						// 双侧完成，记录并检查是否所有肋间都已完成
						percussionProgress.value.completedAreas.push(areaId);
						const totalAreas = percussionPointsSide.value[VIEW_TYPES.SIDE].areas.length;

						if (percussionProgress.value.completedAreas.length === totalAreas) {
							// 所有肋间双侧都完成，切换到背面
							switchToNextPercussionView();
						} else {
							// 移动到下一个肋间的左侧
							percussionProgress.value.currentArea++;
							currentPercussionSide.value = 'left';
							percussionProgress.value.clickedIds = [];
							uni.showModal({
								title: '右侧叩诊完成，请你转身',
								showCancel: false
							});
						}
					}
				}
			} else {
				// 未完成当前侧，设置动画隐藏
				clearTimeout(percussionHandTimer.value);
				percussionHandTimer.value = setTimeout(() => {
					isPercussionActive.value = false;
					percussionHandVisible.value = false;
				}, 1200);
			}
		} else {
			// 正面和背面视图处理逻辑保持不变
			if (percussionProgress.value.clickedIds.length === expectedLength) {
				await delay(1200);
				isPercussionActive.value = false;
				percussionHandVisible.value = false;

				percussionProgress.value.completedAreas.push(areaId);
				percussionProgress.value.currentArea++;
				percussionProgress.value.clickedIds = [];

				const allAreas = getCurrentPercussionAreas();
				if (percussionProgress.value.currentArea > allAreas.length) {
					switchToNextPercussionView();
				} else {
					uni.showModal({
						title: `${area.text}叩诊完成`,
						showCancel: false
					});
				}
			} else {
				clearTimeout(percussionHandTimer.value);
				percussionHandTimer.value = setTimeout(() => {
					isPercussionActive.value = false;
					percussionHandVisible.value = false;
				}, 1200);
			}
		}
	};
	// 重置叩诊进度
	const resetPercussionProgress = () => {
		percussionProgress.value = {
			currentArea: 1,
			clickedIds: [],
			completedAreas: [],
			completedSidesPerArea: {} // 保留此状态用于跟踪双侧完成情况
		};
	};
	// 新增：检查部分序列是否有效
	const checkPartialSequence = (areaId, sequence) => {
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			// 侧面视图使用侧面专用规则
			const sequences = sideValidSequences[areaId];
			if (!sequences) return true; // 如果没有定义规则，默认允许

			// 检查这个部分序列是否是任何有效序列的开头
			return sequences.some(validSeq => {
				return sequence.every((id, index) => validSeq[index] === id);
			});
		} else if (currentPercussionView.value === VIEW_TYPES.BACK) {
			// 背面视图使用背面专用规则
			const sequences = backValidSequences[areaId];
			if (!sequences) return true;

			return sequences.some(validSeq => {
				return sequence.every((id, index) => validSeq[index] === id);
			});
		} else {
			// 正面和背面视图使用原有规则
			const sequences = frontValidSequences[areaId];
			if (!sequences) return true;

			return sequences.some(validSeq => {
				return sequence.every((id, index) => validSeq[index] === id);
			});
		}
	};

	// 获取当前叩诊提示信息
	const getCurrentPercussionHint = computed(() => {
		const currentArea = percussionProgress.value.currentArea;
		const clickedCount = percussionProgress.value.clickedIds.length;

		// 侧面视图特殊处理
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			const sideText = currentPercussionSide.value === 'left' ? '左侧' : '右侧';
			const viewName = currentPercussionSide.value === 'left' ?
				'胸廓左侧面' : '胸廓右侧面';

			if (currentArea > getCurrentPercussionAreas().length) {
				return `${viewName}叩诊已完成`;
			}

			const area = percussionPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === currentArea);
			const areaText = area?.text || '';
			const totalPoints = currentPercussionSide.value === 'left' ?
				area.sides.left.length : area.sides.right.length;

			return `${viewName} - ${sideText} - ${areaText}`; // (${clickedCount}/${totalPoints})
		}
		// 正面和背面视图保持原有逻辑
		const viewName = percussionImagesData[
			currentPercussionView.value === VIEW_TYPES.FRONT ? 0 : 3
		].title;
		// 正面和背面视图保持原有逻辑
		if (currentArea > getCurrentPercussionAreas().length) {
			return `${viewName}叩诊已完成`;
		}

		const areaText = getCurrentPercussionAreas().find(a => a.id === currentArea)?.text || '';
		const totalPoints = getCurrentPercussionAreas().find(a => a.id === currentArea)?.rects.length || 0;

		return `${viewName} - ${areaText}`; // (${clickedCount}/${totalPoints})
	});
	// 获取当前肺下界叩诊区域
	const getCurrentLowerBoundAreas = () => {
		switch (currentPercussionView.value) {
			case VIEW_TYPES.FRONT:
				return lowerBoundPointsFront.value[currentPercussionView.value]?.areas || [];
			case VIEW_TYPES.SIDE:
				const sideAreas = lowerBoundPointsSide.value[currentPercussionView.value]?.areas || [];
				return sideAreas.map(area => {
					const sideRects = lowerBoundProgress.value.currentSide === 'left' ?
						area.sides.left : area.sides.right;
					return {
						id: area.id,
						text: area.text,
						rects: sideRects,
						timeout: area.timeout
					};
				});
			case VIEW_TYPES.BACK:
				return lowerBoundPointsBack.value[currentPercussionView.value]?.areas || [];
			default:
				return [];
		}
	};
	// 处理肺下界叩诊点点击
	const switchToNextLowerBoundView = () => {
		if (currentPercussionView.value === VIEW_TYPES.FRONT) {
			// 从正面切换到侧面（从左侧开始）
			currentPercussionView.value = VIEW_TYPES.SIDE;
			lowerBoundProgress.value.currentSide = 'left';
			resetLowerBoundProgress();
			uni.showToast({
				title: '切换到侧面视图，请从左侧开始叩诊',
				icon: 'success'
			});
		} else if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			if (lowerBoundProgress.value.currentSide === 'left') {
				// 完成左侧，切换到右侧
				lowerBoundProgress.value.currentSide = 'right';
				resetLowerBoundProgress();
				uni.showToast({
					title: '左侧叩诊完成，请继续右侧叩诊',
					icon: 'none'
				});
			} else {
				// 完成右侧，切换到背面
				currentPercussionView.value = VIEW_TYPES.BACK;
				percussionImageStyle.value = {
					width: '90%'
				};
				resetLowerBoundProgress();
				uni.showToast({
					title: '切换到背面视图',
					icon: 'success'
				});
			}
		} else {
			// 完成所有叩诊
			setTimeout(() => {
				showLungQuizDialog.value = true;
				quizAnswers.value = {
					clavicular: '',
					axillary: '',
					scapular: ''
				};
				quizResult.value = '';
				quizCorrectCount.value = 0;
			}, 500);
		}
	};
	const handleLowerBoundPointClick = async (areaId, rectIndex, rectId) => {
		const area = getCurrentLowerBoundAreas().find(a => a.id === areaId);
		if (!area) return;

		// 检查是否应该点击当前区域
		if (areaId !== lowerBoundProgress.value.currentArea) {
			uni.showModal({
				title: '顺序错误',
				content: '请按照顺序叩诊',
				showCancel: false
			});
			return;
		}

		// 检查是否已经点击过这个点
		if (lowerBoundProgress.value.clickedIds.includes(rectId)) {
			return;
		}

		const rect = area.rects.find(r => r.id === rectId);
		if (!rect) return;

		uni.showToast({
			title: `${rect.type}`,
			icon: 'none',
			duration: 1200
		});
		// 播放对应音频
		playPercussionSound(rect.type);

		// 显示手部动画
		percussionHandPosition.left = rect.left;
		percussionHandPosition.top = `${parseInt(rect.top) - 20}px`;
		percussionHandVisible.value = true;
		isPercussionActive.value = true;

		// 记录点击
		lowerBoundProgress.value.clickedIds.push(rectId);

		// 检查是否完成当前区域的所有叩诊
		const expectedLength = area.rects.length;
		let isValidSequence = true;

		if (currentPercussionView.value === VIEW_TYPES.BACK) {
			// 背面视图需要检查顺序
			const sequences = lowerBoundBackValidSequences[areaId];
			if (sequences) {
				// 检查当前点击序列是否是有效序列的开头部分
				isValidSequence = sequences.some(validSeq => {
					return lowerBoundProgress.value.clickedIds.every((id, index) =>
						index < validSeq.length && id === validSeq[index]
					);
				});
			}
		}

		if (!isValidSequence) {
			// 顺序错误，重置当前区域
			resetCurrentLowerBoundArea();
			uni.showModal({
				title: "叩诊顺序错误",
				showCancel: false
			});
			return;
		}

		if (lowerBoundProgress.value.clickedIds.length >= expectedLength) {
			// 完成当前区域
			await delay(1200);
			isPercussionActive.value = false;
			percussionHandVisible.value = false;

			lowerBoundProgress.value.completedAreas.push(areaId);
			lowerBoundProgress.value.currentArea++;
			lowerBoundProgress.value.clickedIds = [];

			// 检查是否完成当前视图的所有叩诊
			const allAreas = getCurrentLowerBoundAreas();
			if (lowerBoundProgress.value.currentArea > allAreas.length) {
				// 切换到下一个视图或侧别
				switchToNextLowerBoundView();
			} else {
				uni.showToast({
					title: `${area.text}完成`,
					icon: 'success',
					duration: 1000
				});
			}
		} else {
			// 设置2秒后自动隐藏手部动画
			clearTimeout(percussionHandTimer.value);
			percussionHandTimer.value = setTimeout(() => {
				isPercussionActive.value = false;
				percussionHandVisible.value = false;
			}, 1200);
		}
	};
	const resetCurrentLowerBoundArea = () => {
		// 隐藏手部动画
		isPercussionActive.value = false;
		percussionHandVisible.value = false;
		lowerBoundProgress.value.clickedIds = [];
	};
	const showLungQuizDialog = ref(false);
	const quizAnswers = ref({
		clavicular: '', //锁骨中线
		axillary: '', //腋中线
		scapular: '' //肩胛线
	})
	const quizResult = ref('');
	const quizCorrectCount = ref(0);

	// 提交肺下界叩诊填空题
	const submitLungQuiz = () => {
		// 正确答案
		const correctAnswers = {
			clavicular: 7, // 锁骨中线第7肋间隙
			axillary: 9, // 腋中线第9肋间隙
			scapular: 11 // 肩胛线第11肋间隙
		};

		// 计算答对数量
		let correctCount = 0;

		if (parseInt(quizAnswers.value.clavicular) === correctAnswers.clavicular) {
			correctCount++;
		}

		if (parseInt(quizAnswers.value.axillary) === correctAnswers.axillary) {
			correctCount++;
		}

		if (parseInt(quizAnswers.value.scapular) === correctAnswers.scapular) {
			correctCount++;
		}

		quizCorrectCount.value = correctCount;
		quizResult.value = `答对了 ${correctCount}/3`;

		// 全部答对时显示特殊提示
		if (correctCount === 3) {
			uni.showToast({
				title: '全部答对!',
				icon: 'none',
				duration: 1500
			})
			setTimeout(() => {
				showLungQuizDialog.value = false;
				uni.showToast({
					title: '肺下界叩诊完成！',
					icon: 'success',
					duration: 2000
				});
			}, 2000)
		} else {
			uni.showToast({
				title: `答对了 ${correctCount}/3`,
				icon: 'none',
				duration: 2500
			})
		}
	};

	// 重置肺下界叩诊进度
	const resetLowerBoundProgress = () => {
		lowerBoundProgress.value = {
			currentArea: 1,
			clickedIds: [],
			completedAreas: [],
			currentSide: lowerBoundProgress.value.currentSide // 保持当前侧别
		};
	};

	// 获取当前肺下界叩诊提示
	const getCurrentLowerBoundHint = computed(() => {
		const currentArea = lowerBoundProgress.value.currentArea;
		const clickedCount = lowerBoundProgress.value.clickedIds.length;

		// 获取当前视图名称
		let viewName;
		if (currentPercussionView.value === VIEW_TYPES.FRONT) {
			viewName = '胸廓正面';
		} else if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			viewName = lowerBoundProgress.value.currentSide === 'left' ?
				'胸廓左侧面' : '胸廓右侧面';
		} else {
			viewName = '胸廓背面';
		}

		// 侧面视图特殊处理
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			const sideText = lowerBoundProgress.value.currentSide === 'left' ? '左侧' : '右侧';

			if (currentArea > getCurrentLowerBoundAreas().length) {
				return `${viewName}叩诊已完成`;
			}

			const area = lowerBoundPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === currentArea);
			const areaText = area?.text || '';

			return `${viewName} - ${areaText}`;
		}

		// 正面和背面视图
		if (currentArea > getCurrentLowerBoundAreas().length) {
			return `${viewName}叩诊已完成`;
		}

		const areaText = getCurrentLowerBoundAreas().find(a => a.id === currentArea)?.text || '';

		return `${viewName} - ${areaText}`;
	});
	const audioContexts = new Map(); // 用于管理音频实例
	const audioMap = {
		'过清音': audioResources.hyperresonance,
		'浊音': audioResources.dullness,
		'鼓音': audioResources.tympany,
		'实音': audioResources.flatness,
		'支气管呼吸音': audioResources.bronchialBreath,
		'支气管肺泡呼吸音': audioResources.bronchialAlveolarBreath,
		'肺泡呼吸音': audioResources.vesicularBreath
	};
	const playPercussionSound = (type) => {
		audioContexts.forEach(audio => audio.stop());
		if (audioMap[type]) {
			// 先停止之前同类型的音频
			if (audioContexts.has(type)) {
				audioContexts.get(type).stop();
			}

			const innerAudioContext = uni.createInnerAudioContext();
			audioContexts.set(type, innerAudioContext);

			innerAudioContext.src = audioMap[type];
			innerAudioContext.volume = 1;
			innerAudioContext.format = 'm4a'; // 明确指定格式

			// 错误处理
			innerAudioContext.onError((err) => {
				console.error('音频播放失败:', err);
				uni.showToast({
					title: '音频播放失败',
					icon: 'none'
				});
			});

			innerAudioContext.play();

			// 播放结束后销毁
			innerAudioContext.onEnded(() => {
				innerAudioContext.destroy();
				audioContexts.delete(type);
			});
		}
	};
	onMounted(() => {
		// 预加载所有音频
		const audioTypes = ['过清音', '浊音', '鼓音', '实音'];
		audioTypes.forEach(type => {
			const audio = uni.createInnerAudioContext();
			audio.src = audioMap[type];
			audio.format = 'm4a';
			audio.autoplay = false;
			audio.preload = true;

			audio.onCanplay(() => {
				console.log(`${type}音频预加载完成`);
			});

			audio.onError((err) => {
				console.error(`${type}音频预加载失败:`, err);
			});
		});
	});
	// 测试音频播放
	const testAudio = (type) => {
		playPercussionSound(type);
		uni.showToast({
			title: `正在播放${type}`,
			icon: 'none'
		});
	};
	//触觉语颤状态
	const currentTactileViewIndex = ref(0);
	const leftTactileHandPosition = reactive({
		x: 50,
		y: 300
	});
	const rightTactileHandPosition = reactive({
		x: 100,
		y: 300
	});
	const tactileHandInPosition = reactive({
		left: false,
		right: false
	});
	const currentTactileGroup = ref(1);
	const tactileCompleted = ref(false);
	const isReversePosition = ref(false);
	// 正面目标位置 (6个位置，3组)
	const tactileFrontPositions = ref([
		// 第一组
		{
			group: 1,
			positions: [{
					hand: 'left',
					left: '120px',
					top: '80px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '180px',
					top: '80px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		},
		// 第二组
		{
			group: 2,
			positions: [{
					hand: 'left',
					left: '110px',
					top: '120px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '190px',
					top: '120px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		},
		// 第三组
		{
			group: 3,
			positions: [{
					hand: 'left',
					left: '100px',
					top: '160px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '200px',
					top: '160px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		}
	]);

	// 背面目标位置 (8个位置，4组)
	const tactileBackPositions = ref([
		// 第一组
		{
			group: 1,
			positions: [{
					hand: 'left',
					left: '120px',
					top: '80px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '180px',
					top: '80px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		},
		// 第二组
		{
			group: 2,
			positions: [{
					hand: 'left',
					left: '110px',
					top: '120px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '190px',
					top: '120px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		},
		// 第三组
		{
			group: 3,
			positions: [{
					hand: 'left',
					left: '100px',
					top: '160px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '200px',
					top: '160px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		},
		// 第四组
		{
			group: 4,
			positions: [{
					hand: 'left',
					left: '90px',
					top: '200px',
					width: 40,
					height: 40,
					color: 'blue'
				},
				{
					hand: 'right',
					left: '210px',
					top: '200px',
					width: 40,
					height: 40,
					color: 'blue'
				}
			]
		}
	]);
	//获取当前视图的目标位置
	const getCurrentTargetPositions = () => {
		const positions = currentTactileViewIndex.value === 0 ? tactileFrontPositions.value :
			tactileBackPositions
			.value;
		return positions.filter(p => p.group === currentTactileGroup.value).flatMap(p => p.positions);
	}
	//获取当前触觉语颤图片
	const getCurrentTactileImage = () => {
		return currentTactileViewIndex.value === 0 ? thoraxImages.front : thoraxImages.back;
	}
	//手部拖动
	const onTactileHandDrag = (e, hand) => {
		const {
			x,
			y
		} = e.detail;
		if (hand === 'left') {
			leftTactileHandPosition.x = x;
			leftTactileHandPosition.y = y;
		} else {
			rightTactileHandPosition.x = x;
			rightTactileHandPosition.y = y;
		}
		checkHandInTactilePosition(hand, x, y);
	}
	const checkHandInTactilePosition = (hand, x, y) => {
		const currentPositions = getCurrentTargetPositions();
		// 如果是反手位置，需要交换手的检测目标
		let targetHand = hand;
		if (isReversePosition.value) {
			targetHand = hand === 'left' ? 'right' : 'left';
		}
		const targetPosition = currentPositions.find(p => p.hand === targetHand);
		if (!targetPosition) return;
		const tolerance = 25;
		const targetLeft = parseInt(targetPosition.left) - tolerance;
		const targetTop = parseInt(targetPosition.top) - tolerance;
		const targetRight = parseInt(targetPosition.left) + targetPosition.width + tolerance;
		const targetBottom = parseInt(targetPosition.top) + targetPosition.height + tolerance;

		const inPosition = x >= targetLeft && x <= targetRight &&
			y >= targetTop && y <= targetBottom;
		tactileHandInPosition[hand] = inPosition;
		if (tactileHandInPosition.left && tactileHandInPosition.right) {
			uni.showToast({
				title: '位置正确！',
				icon: 'success'
			})
		}
	}
	//切换触觉语颤视图
	const switchTactileView = () => {
		currentTactileViewIndex.value = currentTactileViewIndex.value === 0 ? 1 : 0;
		resetTactileHands();
		currentTactileGroup.value = 1;
		isReversePosition.value = false;
	}
	//重置手部位置
	const resetTactileHands = () => {
		leftTactileHandPosition.x = 50;
		leftTactileHandPosition.y = 300;
		rightTactileHandPosition.x = 200;
		rightTactileHandPosition.y = 300;
		tactileHandInPosition.left = false;
		tactileHandInPosition.right = false;
	}
	// 获取操作提示
	const getTactileInstruction = () => {
		if (!tactileHandInPosition.left && !tactileHandInPosition.right) {
			return '请将双手放置在正确位置上';
		} else if (tactileHandInPosition.left && tactileHandInPosition.right) {
			if (!isReversePosition.value) {
				return '位置正确！请说"一"进行检测';
			} else {
				return '反手位置正确！请说"一"进行检测';
			}
		} else {
			return '请将另一只手也放到正确位置';
		}
	};
	//处理触觉语颤语音识别
	const handleTactileVoiceResult = () => {
		if (!showTactileFremitus.value) return;
		const cleanText = voiceResult.value.replace(/[，。、；：'"“”]/g, '').toLowerCase();
		if (cleanText.includes('一')) {
			//播放音频
			playTactileSound();
			setTimeout(() => {
				if (!isReversePosition.value) {
					//切换反手位置
					isReversePosition.value = true;
					reverseHandPositions();
				} else {
					//进入下一组
					completeCurrentGroup();
				}
			}, 2000);
		}
	}
	const playTactileSound = () => {
		const audioContext = uni.createInnerAudioContext();
		audioContext.src = audioResources.hyperresonance; // 需要添加这个音频资源tactileFremitus
		audioContext.play();

		audioContext.onEnded(() => {
			audioContext.destroy();
		});
	}
	//反手位置交换
	const reverseHandPositions = () => {
		// 先回到初始位置
		leftTactileHandPosition.x = 50;
		leftTactileHandPosition.y = 300;
		rightTactileHandPosition.x = 200;
		rightTactileHandPosition.y = 300;

		// 重置位置检测状态
		tactileHandInPosition.left = false;
		tactileHandInPosition.right = false;
	};
	const completeCurrentGroup = () => {
		const toatlGroups = currentTactileViewIndex.value === 0 ? 3 : 4;
		if (currentTactileGroup.value < totalGroups) {
			currentTactileGroup.value++;
			isReversePosition.value = false;
			resetTactileHands();
			uni.showToast({
				title: `开始第${currentTactileGroup.value}组检查`,
				icon: 'none'
			});
		} else {
			if (currentTactileViewIndex.value === 0) {
				// 正面完成，切换到背面
				switchTactileView();
				uni.showModal({
					title: '正面检查完成，开始背面检查',
					showCancel: false
				});
			} else {
				// 背面也完成，全部结束
				tactileCompleted.value = true;
				uni.showToast({
					title: '触觉语颤检查完成！',
					icon: 'success'
				});
			}
		}
	}
	//肺下界移动度状态
	const markedPointsDistance = ref('4.0');
	const isMarkingMode = ref(false);
	const isShowRecording = ref(false);
	const markedPointsLeft = ref('223px');
	const markedPointsTop = ref('222px');
	const markedPointsHeight = ref('50px');
	const lungMobilityState = ref({
		phase: 1,
		currentArea: 1,
		clickedIds: [],
		completedAreas: [],
		markedPoints: [] //已标记的点
	})
	const lungMobilityPoints = ref({
		// 第一次叩诊：从第7肋到第11肋
		1: {
			areas: [{
					id: 1,
					text: "第七肋间隙",
					rects: [{
						id: 1,
						left: '207px',
						top: '155px',
						width: 30,
						height: 18,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 2,
					text: "第八肋间隙",
					rects: [{
						id: 2,
						left: '207px',
						top: '176px',
						width: 30,
						height: 18,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 3,
					text: "第九肋间隙",
					rects: [{
						id: 3,
						left: '207px',
						top: '196px',
						width: 30,
						height: 18,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 4,
					text: "第十肋间隙",
					rects: [{
						id: 4,
						left: '207px',
						top: '216px',
						width: 30,
						height: 18,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 5,
					text: "第十一肋间隙",
					rects: [{
						id: 5,
						left: '207px',
						top: '237px',
						width: 30,
						height: 18,
						color: 'red',
						type: '浊音'
					}],
					timeout: 3000
				}
			],
			targetMark: 5 // 需要标记的第11肋
		},

		// 第二次叩诊：从第11肋到第12肋
		2: {
			areas: [{
					id: 1,
					text: "第十一肋间隙",
					rects: [{
						id: 5,
						left: '207px',
						top: '237px',
						width: 30,
						height: 18,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				},
				{
					id: 2,
					text: "第十二肋间隙",
					rects: [{
						id: 7,
						left: '207px',
						top: '257px',
						width: 30,
						height: 18,
						color: 'red',
						type: '浊音'
					}],
					timeout: 3000
				}
			],
			targetMark: 7 // 需要标记的第12肋
		},

		// 第三次叩诊：从第11肋到第10肋
		3: {
			areas: [{
					id: 1,
					text: "第十一肋间隙",
					rects: [{
						id: 5,
						left: '207px',
						top: '237px',
						width: 30,
						height: 18,
						color: 'red',
						type: '浊音'
					}],
					timeout: 3000
				},
				{
					id: 2,
					text: "第十肋间隙",
					rects: [{
						id: 4,
						left: '207px',
						top: '216px',
						width: 30,
						height: 18,
						color: 'red',
						type: '过清音'
					}],
					timeout: 3000
				}
			],
			targetMark: 4 // 需要标记的第10肋
		}
	});
	const getAllMarkedPointsFromAllPhases = () => {
		const allRects = [];
		// 遍历所有阶段获取已标记的rect信息
		for (let phase = 1; phase <= 3; phase++) {
			const areas = lungMobilityPoints.value[phase].areas;
			areas.forEach(area => {
				area.rects.forEach(rect => {
					if (lungMobilityState.value.markedPoints.includes(rect.id)) {
						allRects.push(rect);
					}
				});
			});
		}
		return allRects;
	};
	//获取当前移动度叩诊区域
	const getCurrentMobilityAreas = () => {
		return lungMobilityPoints.value[lungMobilityState.value.phase]?.areas || [];
	}
	//检查点是否被标记
	const isRectMarked = (rectId) => {
		return lungMobilityState.value.markedPoints.includes(rectId);
	}
	//处理移动度叩诊点点击
	const handleMobilityPointClick = async (areaId, rectIndex, rectId) => {
		if (isMarkingMode.value) return;
		if (isShowRecording.value && activeId.value === 1) return;
		const currentPhase = lungMobilityPoints.value[lungMobilityState.value.phase];
		const area = currentPhase.areas.find(a => a.id === areaId);
		if (!area) return;
		if (areaId !== lungMobilityState.value.currentArea) {
			uni.showModal({
				title: '顺序错误',
				content: '请按照顺序叩诊',
				showCancel: false
			});
			return;
		}
		if (lungMobilityState.value.clickedIds.includes(rectId)) {
			return;
		}
		const rect = area.rects.find(r => r.id === rectId);
		if (!rect) return;
		playPercussionSound(rect.type);
		uni.showToast({
			title: `${rect.type}`,
			icon: 'none'
		});
		// 显示手部动画
		percussionHandPosition.left = rect.left;
		percussionHandPosition.top = `${parseInt(rect.top) - 20}px`;
		percussionHandVisible.value = true;
		isPercussionActive.value = true;
		//记录点击
		lungMobilityState.value.clickedIds.push(rectId);
		await delay(1200);
		isPercussionActive.value = false;
		percussionHandVisible.value = false;
		lungMobilityState.value.completedAreas.push(areaId);
		lungMobilityState.value.currentArea++;
		lungMobilityState.value.clickedIds = [];
		//检查是否完成当前区域的叩诊
		const expectedLength = area.rects.length;

		//检查是否完成当前阶段的所有叩诊
		const allAreas = getCurrentMobilityAreas();
		if (lungMobilityState.value.currentArea > allAreas.length) {
			//开始标记
			isMarkingMode.value = true;
			uni.showModal({
				title: "当前叩诊完成",
				content: "请标记目标肋间隙",
				showCancel: false
			});
		}
	};
	//标记目标
	const markTargetPoint = (rectId) => {
		if (!isMarkingMode.value || lungMobilityState.value.markedPoints.includes(rectId)) return;
		if (isShowRecording.value && activeId.value === 1) return;
		const currentPhase = lungMobilityPoints.value[lungMobilityState.value.phase];
		if (rectId === currentPhase.targetMark) {
			lungMobilityState.value.markedPoints.push(rectId);
			isMarkingMode.value = false;
			uni.showToast({
				title: '标记成功',
				icon: 'success'
			});
			if (lungMobilityState.value.phase < 3) {
				isShowRecording.value = true;
				activeId.value = 1;
			} else {
				isShowRecording.value = false;
				activeId.value = null //取消激活状态
			}
			if (lungMobilityState.value.markedPoints.length === 3) {
				uni.showToast({
					title: '开始测量',
					icon: 'none'
				});
				setTimeout(() => {
					uni.showToast({
						title: '肺下界移动度完成',
						icon: 'none'
					});
				}, 1500)
			} else {
				isShowRecording.value = true;
				activeId.value = 1;
			}
		} else {
			uni.showToast({
				title: '标记错误',
				icon: 'error'
			});
		}
	};
	//切换到下一阶段
	const switchToNextMobilityPhase = () => {
		if (lungMobilityState.value.phase < 3) {
			lungMobilityState.value.phase++;
			lungMobilityState.value.currentArea = 1;
			lungMobilityState.value.clickedIds = [];
			lungMobilityState.value.completeArea = [];
			isMarkingMode.value = false;
			isShowRecording.value = false;
			activeId.value = null //取消激活状态
			uni.showToast({
				title: `开始第${lungMobilityState.value.phase}次叩诊`,
				icon: 'none'
			})
		} else {
			uni.showToast({
				title: '所有叩诊已完成',
				icon: 'success',
				duration: 2000
			})
		}
	}
	const getCurrentMobilityHint = computed(() => {
		const phase = lungMobilityState.value.phase;
		const currentArea = lungMobilityState.value.currentArea;
		const areas = getCurrentMobilityAreas();
		if (isMarkingMode.value) {
			const currentPhase = lungMobilityPoints.value[phase];
			const targetArea = currentPhase.areas.find(a =>
				a.rects.some(r => r.id === currentPhase.targetMark));
			return '请标记肋间隙';
		}
		const areaText = areas.find(a => a.id === currentArea)?.text || '';
		return `第${phase}次叩诊 - ${areaText}`;
	})
	// 关键修改：标记完成后更新尺子/竖线位置（基于实际标记点）
	// watch(() => lungMobilityState.value.markedPoints.length, (len) => {
	// 	if (len === 3) {
	// 		// 从标记点中获取10肋（id4）、11肋（id5）、12肋（id7）的位置
	// 		const rect10 = getRectById(4); // 10肋rect
	// 		const rect12 = getRectById(7); // 12肋rect
	// 		if (rect10 && rect12) {
	// 			markedPointsLeft.value = rect10.left;
	// 			markedPointsTop.value = rect10.top;
	// 			// 计算10肋到12肋的高度差（视觉用）
	// 			markedPointsHeight.value = `${parseInt(rect12.top) - parseInt(rect10.top)}px`;
	// 			markedPointsDistance.value = '4.0';
	// 		}
	// 	}
	// });

	// 辅助：根据id获取rect信息
	const getRectById = (id) => {
		const allAreas = [...lungMobilityPoints.value[1].areas, ...lungMobilityPoints.value[2].areas, ...
			lungMobilityPoints.value[3].areas
		];
		for (const area of allAreas) {
			const rect = area.rects.find(r => r.id === id);
			if (rect) return rect;
		}
		return null;
	};
	const showWholeLungAuscultation = ref(false);
	const handleAuscultationCheck = (item) => {
		// 关闭所有其他视图
		shouldHideCartIcon.value = true;
		showVisualDiagnosis.value = false;
		showChestExpansion.value = false;
		showPleuraExpansion.value = false;
		showLungMobility.value = false;
		showWholeLung.value = false;
		showDynamicImage.value = false;
		showSeat.value = false;
		showSeatUp.value = false;
		voiceResult.value = "";
		// 其他体格检查逻辑...
		layerWidth.value = '100rpx';
		activeId.value = null;
		// 设置纯色背景
		showPlainBackground.value = true;
		// 如果是全肺叩诊
		if (item.text === '全肺听诊') {
			activeId.value = null //取消激活状态
			//显示麦克风
			// activeId.value = 1;
			showWholeLungAuscultation.value = true;
			// 重置状态
			currentViewIndex.value = VIEW_TYPES.FRONT;
			currentPercussionHint.value = '请按顺序听诊各个点位';
		}
	}
	//正面听诊点
	const auscultationPointsFront = ref({
		[VIEW_TYPES.FRONT]: {
			areas: [{
					id: 1,
					text: " ",
					rects: [{
							id: 1,
							left: '109px',
							top: '29px',
							width: 42,
							height: 15,
							color: 'red',
							type: '支气管肺泡呼吸音'
						},
						{
							id: 2,
							left: '176px',
							top: '29px',
							width: 42,
							height: 15,
							color: 'red',
							type: '支气管肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 2,
					text: "第一肋间",
					rects: [{
							id: 3,
							left: '98px',
							top: '50px',
							width: 45,
							height: 20,
							color: 'red',
							type: '支气管肺泡呼吸音'
						},
						{
							id: 4,
							left: '182px',
							top: '50px',
							width: 45,
							height: 20,
							color: 'red',
							type: '支气管肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 3,
					text: "第二肋间",
					rects: [{
							id: 5,
							left: '98px',
							top: '76px',
							width: 50,
							height: 18,
							color: 'red',
							type: '支气管肺泡呼吸音'
						},
						{
							id: 6,
							left: '186px',
							top: '76px',
							width: 50,
							height: 18,
							color: 'red',
							type: '支气管肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 4,
					text: "第三肋间",
					rects: [{
							id: 7,
							left: '228px',
							top: '97px',
							width: 30,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						},
						{
							id: 8,
							left: '195px',
							top: '97px',
							width: 30,
							height: 18,
							color: 'red',
							type: '支气管肺泡呼吸音'
						},
						{
							id: 9,
							left: '132px',
							top: '97px',
							width: 35,
							height: 18,
							color: 'red',
							type: '支气管肺泡呼吸音'
						},
						{
							id: 10,
							left: '89px',
							top: '98px',
							width: 40,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 5,
					text: "第四肋间",
					rects: [{
							id: 11,
							left: '89px',
							top: '120px',
							width: 40,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						},
						{
							id: 12,
							left: '228px',
							top: '119px',
							width: 35,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 6,
					text: "第五肋间",
					rects: [{
							id: 13,
							left: '228px',
							top: '151px',
							width: 35,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						},
						{
							id: 14,
							left: '94px',
							top: '151px',
							width: 35,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 7,
					text: "第六肋间",
					rects: [{
							id: 15,
							left: '107px',
							top: '184px',
							width: 35,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						},
						{
							id: 16,
							left: '209px',
							top: '184px',
							width: 35,
							height: 18,
							color: 'red',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
			]
		},

	});
	//侧面听诊点
	const auscultationPointsSide = ref({
		[VIEW_TYPES.SIDE]: {
			areas: [{
					id: 1,
					text: '腋窝',
					sides: {
						left: [{
							id: 1,
							left: '167px',
							top: '100px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '肺泡呼吸音'
						}],
						right: [{
							id: 2,
							left: '167px',
							top: '100px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '肺泡呼吸音'
						}],

					},
					timeout: 3000
				},
				{
					id: 2,
					text: '第四肋间隙',
					sides: {
						left: [{
							id: 3,
							left: '167px',
							top: '145px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '肺泡呼吸音'
						}],
						right: [{
							id: 4,
							left: '167px',
							top: '145px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '肺泡呼吸音'
						}]

					},
					timeout: 3000
				},
				{
					id: 3,
					text: '第五肋间隙',
					sides: {
						left: [{
							id: 5,
							left: '167px',
							top: '192px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '肺泡呼吸音'
						}],
						right: [{
							id: 6,
							left: '167px',
							top: '192px',
							width: 40,
							height: 25,
							color: 'blue',
							type: '肺泡呼吸音'
						}]
					},
					timeout: 3000
				}
			]
		}
	});
	//背面听诊点
	const auscultationPointsBack = ref({
		[VIEW_TYPES.BACK]: {
			areas: [{
					id: 1,
					text: "第二肋间隙",
					rects: [{
							id: 1,
							left: '136px',
							top: '60px',
							width: 30,
							height: 25,
							color: 'green',
							type: '支气管呼吸音'
						},
						{
							id: 2,
							left: '187px',
							top: '79px',
							width: 30,
							height: 12,
							color: 'green',
							type: '支气管呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 2,
					text: "第三肋间隙",
					rects: [{
							id: 3,
							left: '136px',
							top: '95px',
							width: 30,
							height: 12,
							color: 'green',
							type: '支气管肺泡呼吸音'
						},
						{
							id: 4,
							left: '187px',
							top: '95px',
							width: 30,
							height: 12,
							color: 'green',
							type: '支气管肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 3,
					text: "第四肋间隙",
					rects: [{
							id: 5,
							left: '136px',
							top: '111px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						},
						{
							id: 6,
							left: '187px',
							top: '111px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 4,
					text: "第五肋间隙",
					rects: [{
							id: 7,
							left: '136px',
							top: '127px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						},
						{
							id: 8,
							left: '187px',
							top: '127px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 5,
					text: "第六肋间隙",
					rects: [{
							id: 9,
							left: '136px',
							top: '142px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						},
						{
							id: 10,
							left: '187px',
							top: '142px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				},
				{
					id: 6,
					text: "第七肋间隙",
					rects: [{
							id: 11,
							left: '136px',
							top: '158px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						},
						{
							id: 12,
							left: '187px',
							top: '158px',
							width: 30,
							height: 12,
							color: 'green',
							type: '肺泡呼吸音'
						}
					],
					timeout: 3000
				}
			]
		}
	});

	//定义每个肋间的有效点击顺序
	const frontValidAuscultationSequences = {
		1: [
			[1, 2],
			[2, 1]
		],
		2: [
			[3, 4],
			[4, 3]
		],
		3: [
			[5, 6],
			[6, 5]
		],
		4: [
			[10, 9, 8, 7],
			[7, 8, 9, 10],
			[10, 9, 7, 8],
			[7, 8, 10, 9],
			[10, 7, 8, 9],
			[10, 7, 9, 8],
			[7, 10, 8, 9],
			[7, 10, 9, 8]
		],
		5: [
			[11, 12],
			[12, 11]
		],
		6: [
			[13, 14],
			[14, 13]
		],
		7: [
			[15, 16],
			[16, 15]
		]
	};
	// 为侧面叩诊定义专门的顺序规则
	const sideValidAuscultationSequences = {
		1: [ // 腋窝 - 只有1个点，只需点击即可
			[1],
			[2] // 左侧点1，右侧点2
		],
		2: [ // 第4肋间隙 - 只有1个点
			[3],
			[4] // 左侧点3，右侧点4
		],
		3: [
			[5],
			[6]
		]
	};
	// 背面叩诊顺序规则
	const backValidAuscultationSequences = {
		1: [ // 第2肋间隙 - 2个点，任意顺序
			[1, 2],
			[2, 1]
		],
		2: [ // 第3肋间隙 - 2个点，任意顺序
			[3, 4],
			[4, 3]
		],
		3: [ // 第4肋间隙 - 2个点，任意顺序
			[5, 6],
			[6, 5]
		],
		4: [ // 第5肋间隙 - 2个点，任意顺序
			[7, 8],
			[8, 7]
		],
		5: [ // 第6肋间隙 - 2个点，任意顺序
			[9, 10],
			[10, 9]
		],
		6: [ // 第7肋间隙 - 2个点，任意顺序
			[11, 12],
			[12, 11]
		]
	};
	const resetCurrentAuscultationArea = () => {
		percussionProgress.value.clickedIds = [];
		uni.showModal({
			title: "听诊顺序错误，请重新听诊当前肋间",
			showCancel: false
		});
	}
	const auscultationProgress = ref({
	    currentArea: 1,
	    clickedIds: [],
	    completedAreas: [],
	    completedSidesPerArea: {} // 跟踪每个区域的双侧完成状态
	});
	const resetAuscultationProgress = () => {
	    auscultationProgress.value = {
	        currentArea: 1,
	        clickedIds: [],
	        completedAreas: [],
	        completedSidesPerArea: {}
	    };
	};

	const handleAuscultationPointClick = async (areaId, rectIndex, rectId) => {
	    // 获取当前肋间数据
	    const area = getCurrentAuscultationAreas().find(a => a.id === areaId);
	    if (!area) return;
	    
	    // 检查是否应该点击当前肋间
	    if (areaId !== auscultationProgress.value.currentArea) {
	        uni.showModal({
	            title: '顺序错误',
	            content: '请按顺序从当前肋间开始听诊',
	            showCancel: false
	        });
	        return;
	    }
	
	    // 检查是否已经点击过这个点
	    if (auscultationProgress.value.clickedIds.includes(rectId)) {
	        return;
	    }
	
	    const rect = area.rects.find(r => r.id === rectId);
	    if (!rect) return;
	
	    // 临时保存当前点击序列
	    const tempSequence = [...auscultationProgress.value.clickedIds, rectId];
	
	    // 检查当前点击是否有效
	    const isValidClick = checkAuscultationPartialSequence(areaId, tempSequence);
	    if (!isValidClick) {
	        resetCurrentAuscultationArea();
	        return;
	    }
		// 显示手部动画
		percussionHandPosition.left = rect.left;
		percussionHandPosition.top = `${parseInt(rect.top) - 20}px`;
		percussionHandVisible.value = true;
	    // 有效点击处理
	    uni.showToast({
	        title: `${rect.type}`,
	        icon: 'none',
	        duration: 1200
	    });
	    auscultationProgress.value.clickedIds.push(rectId);
	
	    // 播放对应音频
	    playPercussionSound(rect.type);
	
	
	    // 计算当前区域应有点数
	    let expectedLength = currentPercussionView.value === VIEW_TYPES.SIDE 
	        ? (currentPercussionSide.value === 'left' 
	            ? auscultationPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === areaId)?.sides.left.length 
	            : auscultationPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === areaId)?.sides.right.length)
	        : area.rects.length;
	
	    // 核心逻辑：处理侧面视图的交替听诊
	    if (currentPercussionView.value === VIEW_TYPES.SIDE) {
	        if (auscultationProgress.value.clickedIds.length === expectedLength) {
	            // 当前侧完成
	            await delay(6000);
	            percussionHandVisible.value = false;
	
	            // 记录当前肋间的完成侧
	            auscultationProgress.value.completedSidesPerArea[areaId] = {
	                ...auscultationProgress.value.completedSidesPerArea[areaId],
	                [currentPercussionSide.value]: true
	            };
	
	            // 检查是否双侧都已完成
	            const bothSidesComplete = auscultationProgress.value.completedSidesPerArea[areaId]?.left 
	                                    && auscultationProgress.value.completedSidesPerArea[areaId]?.right;
	
	            if (currentPercussionSide.value === 'left') {
	                // 左侧完成，切换到同一肋间的右侧
	                currentPercussionSide.value = 'right';
	                auscultationProgress.value.clickedIds = [];
	                uni.showModal({
	                    title: '左侧听诊完成',
	                    content: `请继续${area.text}的右侧听诊`,
	                    showCancel: false
	                });
	            } else {
	                // 右侧完成
	                if (bothSidesComplete) {
	                    // 双侧完成，记录并检查是否所有肋间都已完成
	                    auscultationProgress.value.completedAreas.push(areaId);
	                    const totalAreas = auscultationPointsSide.value[VIEW_TYPES.SIDE].areas.length;
	                    
	                    if (auscultationProgress.value.completedAreas.length === totalAreas) {
	                        // 所有肋间双侧都完成，切换到背面
	                        switchToNextAuscultationView(); // 新增听诊专用视图切换
	                    } else {
	                        // 移动到下一个肋间的左侧
	                        auscultationProgress.value.currentArea++;
	                        currentPercussionSide.value = 'left';
	                        auscultationProgress.value.clickedIds = [];
	                        uni.showModal({
	                            title: `${area.text}双侧听诊完成`,
	                            content: `请开始第${auscultationProgress.value.currentArea}个肋间的左侧听诊`,
	                            showCancel: false
	                        });
	                    }
	                }
	            }
	        } else {
	            // 未完成当前侧，设置动画隐藏
	            clearTimeout(percussionHandTimer.value);
	            percussionHandTimer.value = setTimeout(() => {
	                percussionHandVisible.value = false;
	            }, 6000);
	        }
	    } else {
	        // 正面和背面视图处理逻辑
	        if (auscultationProgress.value.clickedIds.length === expectedLength) {
	            await delay(6000);
	            percussionHandVisible.value = false;
	
	            auscultationProgress.value.completedAreas.push(areaId);
	            auscultationProgress.value.currentArea++;
	            auscultationProgress.value.clickedIds = [];
	
	            const allAreas = getCurrentAuscultationAreas();
	            if (auscultationProgress.value.currentArea > allAreas.length) {
	                switchToNextAuscultationView();
	            } else {
	                uni.showModal({
	                    title: `${area.text}听诊完成`,
	                    showCancel: false
	                });
	            }
	        } else {
	            clearTimeout(percussionHandTimer.value);
	            percussionHandTimer.value = setTimeout(() => {
	                percussionHandVisible.value = false;
	            }, 6000);
	        }
	    }
	};
	const switchToNextAuscultationView = () => {
	    if (currentPercussionView.value === VIEW_TYPES.FRONT) {
	        currentPercussionView.value = VIEW_TYPES.SIDE;
	        currentPercussionSide.value = 'left';
	        resetAuscultationProgress();
	        uni.showToast({
	            title: '切换到左侧面视图，请开始听诊',
	            icon: 'success'
	        });
	    } else if (currentPercussionView.value === VIEW_TYPES.SIDE) {
	        currentPercussionView.value = VIEW_TYPES.BACK;
	        percussionImageStyle.value = { width: '90%' };
	        resetAuscultationProgress();
	        uni.showToast({
	            title: '侧面听诊完成，切换到背面视图',
	            icon: 'success'
	        });
	    } else {
	        uni.showToast({
	            title: '全肺听诊完成！',
	            icon: 'none',
	            duration: 2000
	        });
	    }
	};
const getCurrentAuscultationAreas = () => {
    switch (currentPercussionView.value) {
        case VIEW_TYPES.FRONT:
            return auscultationPointsFront.value[currentPercussionView.value]?.areas || [];
        case VIEW_TYPES.SIDE:
            const sideAreas = auscultationPointsSide.value[currentPercussionView.value]?.areas || [];
            return sideAreas.map(area => {
                const sideRects = currentPercussionSide.value === 'left' ?
                    area.sides.left : area.sides.right;
                return {
                    id: area.id,
                    text: area.text,
                    rects: sideRects,
                    timeout: area.timeout
                };
            });
        case VIEW_TYPES.BACK:
            return auscultationPointsBack.value[currentPercussionView.value]?.areas || [];
        default:
            return [];
    }
};
	const checkAuscultationPartialSequence = (areaId, sequence) => {
		if (currentPercussionView.value === VIEW_TYPES.SIDE) {
			// 侧面视图使用侧面专用规则
			const sequences = sideValidAuscultationSequences[areaId];
			if (!sequences) return true; // 如果没有定义规则，默认允许

			// 检查这个部分序列是否是任何有效序列的开头
			return sequences.some(validSeq => {
				return sequence.every((id, index) => validSeq[index] === id);
			});
		} else if (currentPercussionView.value === VIEW_TYPES.BACK) {
			// 背面视图使用背面专用规则
			const sequences = backValidAuscultationSequences[areaId];
			if (!sequences) return true;

			return sequences.some(validSeq => {
				return sequence.every((id, index) => validSeq[index] === id);
			});
		} else {
			// 正面和背面视图使用原有规则
			const sequences = frontValidAuscultationSequences[areaId];
			if (!sequences) return true;

			return sequences.some(validSeq => {
				return sequence.every((id, index) => validSeq[index] === id);
			});
		}
	};
	// 获取当前叩诊提示信息
	const getCurrentAuscultationHint = computed(() => {
	    const currentArea = auscultationProgress.value.currentArea;
	    const clickedCount = auscultationProgress.value.clickedIds.length;
	
	    if (currentPercussionView.value === VIEW_TYPES.SIDE) {
	        const sideText = currentPercussionSide.value === 'left' ? '左侧' : '右侧';
	        const viewName = currentPercussionSide.value === 'left' ?
	            '胸廓左侧面' : '胸廓右侧面';
	
	        if (currentArea > getCurrentAuscultationAreas().length) {
	            return `${viewName}听诊已完成`;
	        }
	
	        const area = auscultationPointsSide.value[VIEW_TYPES.SIDE].areas.find(a => a.id === currentArea);
	        const areaText = area?.text || '';
	        const totalPoints = currentPercussionSide.value === 'left' ?
	            area.sides.left.length : area.sides.right.length;
	
	        return `${viewName} - ${sideText} - ${areaText}(${clickedCount}/${totalPoints})`;
	    }
	    
	    const viewName = percussionImagesData[
	        currentPercussionView.value === VIEW_TYPES.FRONT ? 0 : 3
	    ].title;
	    
	    if (currentArea > getCurrentAuscultationAreas().length) {
	        return `${viewName}听诊已完成`;
	    }
	
	    const areaText = getCurrentAuscultationAreas().find(a => a.id === currentArea)?.text || '';
	    const totalPoints = getCurrentAuscultationAreas().find(a => a.id === currentArea)?.rects.length || 0;
	
	    return `${viewName} - ${areaText}(${clickedCount}/${totalPoints})`;
	});
	// 监听语音识别结果
	watch(voiceResult, (newVal) => {
		if (!newVal) return;
		console.log('触发了voiceResult');
		if (!allViewsCompleted.value && showVisualDiagnosis.value) {
			// 去除标点符号并转为小写
			const cleanText = newVal.replace(/[，。、；：'"“”]/g, '').toLowerCase();
			lastVoiceResult.value = cleanText;
			const currentAreas = visualDiagnosisRibs.value[currentViewIndex.value]?.areas || [];
			const matchedArea = currentAreas.find(area => {
				const areaText = area.text.toLowerCase();
				return areaText.includes(cleanText) || cleanText.includes(areaText);
			});
			if (matchedArea) {
				currentExpectedArea.value = matchedArea.id;
				console.log('匹配成功');
				showRectHint.value = true;
				rectHintText.value = `请标注${matchedArea.text}的位置`;
				setTimeout(() => {
					showRectHint.value = false;
				}, 2000);
				voiceResult.value = "";
			} else {
				setTimeout(() => {
					voiceResult.value = "";
				}, 2000);

			}
			return;
		}

		if (allViewsCompleted.value && showVisualDiagnosis.value) {
			// 延迟检查以确保用户完成说话
			setTimeout(checkVoiceResult, 1500);
			return;
		}
		// 更宽松的匹配方式 - 只转为小写，不处理空格和标点
		const lowerVal = newVal.toLowerCase();

		// 暖手操作的容错关键词（只要包含任意一个词就触发）
		const warmHandKeywords = ['暖手', '难受', '男生', '哪一首', '晚上', '暖受', '暖首', '来一首'];

		// 检查暖手指令（使用some+includes实现宽松匹配）
		if (showChestExpansion.value && !warmHandsCompleted.value &&
			warmHandKeywords.some(keyword => lowerVal.includes(keyword))) {
			startWarmHandsAnimation();
			setTimeout(() => {
				voiceResult.value = "";
			}, 1000);
			return;
		}

		// 深呼吸的容错关键词
		const deepBreathKeywords = ['呼吸', '深呼吸', '身呼吸', '深户西', '申呼吸'];

		// 检查深呼吸指令
		if (showChestExpansion.value && chestGame.isCompleted &&
			deepBreathKeywords.some(keyword => lowerVal.includes(keyword))) {
			uni.showToast({
				title: '正确',
				icon: 'success'
			});
			setTimeout(() => {
				showBreathingAnimation.value = true;
			}, 1000);

			setTimeout(() => {
				showBreathingAnimation.value = false;
				voiceResult.value = "";
				currentThoraxIndex.value === 0 ? switchView() : showReportResultStage();
			}, 8000);
			return;
		}

		// 汇报结果检查
		if (showReportResult.value) {
			if (newVal.includes('患者双侧胸廓扩张度受限')) {
				uni.showModal({
					title: '回答正确，胸廓扩张度检查完成',
					showCancel: false
				});
				setTimeout(() => {
					completeChestExpansionCheck();
				}, 4000);
				// } else {
				// 	uni.showToast({
				// 		title: '回答错误',
				// 		icon: 'none'
				// 	});
			}
		}
		// 检查是否在胸膜摩擦感检查中且双手已放置正确
		if (showPleuraExpansion.value && handsPlacedCorrectly.value && deepBreathKeywords.some(keyword =>
				lowerVal
				.includes(keyword))) {
			uni.showToast({
				title: '正确',
				icon: 'success'
			})
			// 开始呼吸动画流程
			setTimeout(() => {
				startBreathingAnimation();
			}, 2000)
		}
		// 添加触觉语颤语音处理
		if (showTactileFremitus.value) {
			handleTactileVoiceResult();
		}
		if (showLungMobility.value && lungMobilityState.value.phase < 3) {
			if (newVal.includes('深呼气') && newVal.includes('屏住呼吸')) {
				uni.showToast({
					title: '正确',
					icon: 'none'
				})
				setTimeout(() => {
					voiceResult.value = '';
					switchToNextMobilityPhase();
				}, 2000);
			}
		}
	});

	// 完成检查
	const completeChestExpansionCheck = () => {
		showReportResult.value = false;
		voiceResult.value = "";
	};
</script>

<style lang="scss" scoped>
	.mova {
		position: fixed;
		top: 0;
		left: 0;
		right: 0;
		bottom: 0;
		z-index: 99;
		/* 确保可拖动区域在顶部 */
		width: 100%;
		height: 100%;
	}

	.ward-container {
		position: fixed;
		width: 100%;
		height: 305rpx;
		background-image: url('https://i.postimg.cc/nzQ9F9jz/image-png-2.png');
		background-position: center;
		background-repeat: no-repeat;
		background-size: cover;
		z-index: 1;
		transition: background-color 0.3s ease, background-image 0.3s ease;

		.seat {
			position: fixed;
			// background-image: url('https://i.postimg.cc/W1Fg6LxM/image-png-8.png');
			right: 470rpx;
			top: 65rpx;
			width: 200rpx;
			height: 240rpx;
			// border: #2A82E4 solid 1rpx;
			z-index: 11;

			.seat-up-img {
				width: 100%;
				height: 100%;

			}
		}

		.seat-up {
			position: fixed;
			// background-image: url('https://i.postimg.cc/d3BJdptr/image-png-9.png');
			right: 470rpx;
			top: 65rpx;
			width: 200rpx;
			height: 240rpx;
			// border: black solid 3rpx;
			background-repeat: no-repeat;
			/* 防止图片重复 */
			z-index: 11;

			.seat-up-img {
				width: 100%;
				height: 100%;
				// // object-fit: contain; /* 保持比例，完整显示图片 */
				// object-fit: cover; //覆盖容器（可能裁剪图片
				// display: block;

			}
		}

		.wrist {
			position: fixed;
			left: 190rpx;
			bottom: 35rpx;
			width: 12rpx;
			height: 12rpx;
			border-radius: 50rpx;
			background-color: #fff;
			filter: blur(1px);
			/* 模糊半径 */
			z-index: 10;
			/* 确保在背景上面，弹幕下面 */
			// pointer-events: auto; /* 确保点击事件有效 */
		}

		.time-module {
			position: absolute;
			right: 1080rpx;
		}

		// 语音按钮
		.voice {
			position: fixed;
			display: flex;
			justify-content: center;
			align-items: center;
			bottom: 5rpx;
			right: 104rpx;
			width: 60rpx;
			height: 60rpx;
			border-radius: 50rpx;
			background-color: #fff;
			opacity: 0;
			visibility: hidden;
			transition: opacity 0.5s ease, visibility 0.5s ease;
			z-index: 170; // 确保可点击

			.voice-icon {
				transform: translate(-50%, -50%);
				position: absolute;
				top: 50%;
				left: 50%;
			}
		}

		// 语音识别结果显示区域
		.voice-result {
			position: fixed;
			top: 80rpx;
			left: 40rpx;
			width: 500rpx;
			max-height: 400rpx;
			background-color: rgba(255, 255, 255, 0.9);
			border-radius: 10rpx;
			padding: 20rpx;
			z-index: 5;
			box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);

			.result-title {
				font-size: 28rpx;
				font-weight: bold;
				color: #2A82E4;
				margin-bottom: 10rpx;
			}

			.result-content {
				font-size: 26rpx;
				color: #333;
				line-height: 1.5;
			}
		}



		.measurement-countdown {
			background-color: black;
			position: fixed;
			top: 20rpx;
			right: 400rpx;
			border: #2A82E4 solid 1rpx;
			padding: 10rpx;
			border-radius: 15rpx;
			z-index: 10;
		}

		// 目标位置样式
		.target-location {
			position: fixed;
			bottom: 10rpx;
			left: 130rpx;
			width: 100rpx;
			height: 100rpx;
			border: 2rpx dashed #999;
			border-radius: 10rpx;
			transition: all 0.3s ease;
			display: flex;
			align-items: center;
			justify-content: center;
			z-index: 5;

			&::before {
				content: '';
				position: absolute;
				width: 70%;
				height: 70%;
				border: 2rpx dashed rgba(0, 0, 0, 0.2);
				border-radius: 6rpx;
			}

			// 接近目标时的样式
			&.target-active {
				border-color: #2A82E4;
				background-color: rgba(42, 130, 228, 0.1);
				transform: scale(1.05);

				&::before {
					border-color: rgba(42, 130, 228, 0.3);
				}
			}

			// 成功放置时的样式
			&.target-complete {
				border-color: #4CAF50;
				border-style: solid;
				background-color: rgba(76, 175, 80, 0.1);

				&::before {
					border-color: rgba(76, 175, 80, 0.3);
					border-style: solid;
				}
			}
		}

		// 拖动元素样式
		.thermometer {
			position: absolute;
			width: 60rpx;
			height: 60rpx;
			z-index: 10;
			// border-radius: 8rpx;
			// box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.1);

			// 拖动提示文字
			.drag-hint {
				position: absolute;
				white-space: nowrap;
				top: -40rpx;
				left: 50%;
				transform: translateX(-50%);
				background-color: rgba(0, 0, 0, 0.7);
				color: white;
				padding: 6rpx 12rpx;
				border-radius: 6rpx;
				font-size: 10rpx;
				animation: hint-fade 1.5s ease infinite;
			}
		}

		@keyframes hint-fade {

			0%,
			100% {
				opacity: 0.5;
			}

			50% {
				opacity: 1;
			}
		}

		// 气泡通用样式
		.bubble-title {
			font-size: 16rpx;
			font-weight: bold;
			color: #2A82E4;
			margin-bottom: 8rpx;
		}

		// 测量结果气泡
		.measurement-bubble {
			position: fixed;
			top: 70rpx;
			left: 60rpx;
			z-index: 12;
			animation: bubble-appear 0.5s ease;
			display: inline-block;
			/* 使整个气泡容器自适应内容 */

			.bubble-content {
				background-color: white;
				padding: 10rpx 20rpx;
				border-radius: 15rpx;
				box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
				display: inline-block;
				/* 使容器宽度自适应内容 */
				max-width: 400rpx;
				/* 设置最大宽度，防止过长 */
				min-width: 80rpx;
				/* 设置最小宽度 */

				text {
					color: #333;
					font-size: 15rpx;
					word-break: break-word;
					/* 确保长单词也能换行 */
					white-space: normal;
					/* 允许文本换行 */
					line-height: 1.5;
					/* 增加行高，提高可读性 */
				}

				.bubble-text {
					color: #333;
					font-size: 18rpx;
					word-break: break-word;
					white-space: normal;
					line-height: 1.5;
				}
			}

			// .bubble-arrow {
			// 	width: 0;
			// 	height: 0;
			// 	border-left: 15rpx solid transparent;
			// 	border-right: 15rpx solid transparent;
			// 	border-top: 15rpx solid white;
			// 	position: absolute;
			// 	bottom: -15rpx;
			// 	left: 30rpx; /* 固定箭头位置在左侧 */
			// }
		}

		@keyframes bubble-appear {
			0% {
				opacity: 0;
				transform: translateY(-20rpx) scale(0.9);
			}

			100% {
				opacity: 1;
				transform: translateY(0) scale(1);
			}
		}

		@keyframes bubble-hide {
			0% {
				opacity: 1;
				transform: translateY(0) scale(1);
			}

			100% {
				opacity: 0;
				transform: translateY(-20rpx) scale(0.9);
			}
		}

		// 医疗车覆盖层
		.overlay {
			position: fixed;
			/* background-image: url('https://i.postimg.cc/W1LRwfRh/image-png-3.png'); */
			right: 300rpx;
			top: 180rpx;
			width: 200rpx;
			height: 160rpx;
			border-radius: 20rpx;
			// box-shadow: 0 2rpx 10rpx rgba(0,0,0,0.1);
			padding: 0;
			z-index: 0;
			display: flex;
			align-items: flex-end;
			justify-content: center;
			background-size: contain;
			background-repeat: no-repeat;
			background-position: center;
		}

		.medical-vehicle {
			position: fixed;
			display: flex;
			align-items: center;
			justify-content: center;
			/* background-image: url('https://i.postimg.cc/vBVcFtXR/20250718153158.png'); */
			right: 100rpx;
			width: 280rpx;
			height: 100%;
			padding: 15rpx 0 0 0;
			// background-color: rgba(255, 255, 255, );
			backdrop-filter: blur(20px);
			background-size: contain;
			background-repeat: no-repeat;
			background-position: center;
			z-index: 5;

			.medical-middle-img {
				position: absolute;
				left: 170rpx;
				top: 50%;
				transform: translate(-50%, -50%);
				width: 200rpx;
				height: 130rpx;
				z-index: 10;
				pointer-events: none;
			}

		}

		//题目
		.title-container {
			position: fixed;
			right: 100rpx;
			width: 280rpx;
			height: 100%;
			padding: 15rpx 0 0 0;
			background-color: rgba(255, 255, 255, 0.4);
			// border: #4CAF50 solid 1rpx;
			backdrop-filter: blur(20px);
			z-index: 15;

			.title-header {
				display: flex;
				justify-content: space-between;
				align-items: center;
				padding: 0 20rpx 20rpx 20rpx;
				border-bottom: 1rpx solid #e0e0e0;

				.title-text {
					font-size: 18rpx;
					font-weight: bold;
					color: #2A82E4;
				}

				.question-counter {
					font-size: 14rpx;
					color: #666;
				}
			}

			.title-scroll-content {
				height: calc(100% - 80rpx);
				/* 减去header的高度 */
				padding: 0;
			}

			.question-content {
				padding: 20rpx;

				.question-text {
					font-size: 16rpx;
					color: #333;
					line-height: 1.5;
					margin-bottom: 30rpx;
				}

				.options-container {
					margin-bottom: 30rpx;

					.option-item {
						height: 10rpx;
						display: flex;
						align-items: center;
						padding: 15rpx;
						margin-bottom: 15rpx;
						background-color: #fff;
						border-radius: 8rpx;
						border: 2rpx solid transparent;
						transition: all 0.3s ease;

						&.selected {
							background-color: #e3f2fd;
							border-color: #2196f3;
						}

						&.correct {
							background-color: #e8f5e8;
							border-color: #4caf50;
						}

						&.incorrect {
							background-color: #ffebee;
							border-color: #f44336;
						}

						.option-id {
							// width: 20rpx;
							// height: 20rpx;
							display: flex;
							align-items: center;
							justify-content: center;
							// background-color: #fff;
							// border-radius: 50%;
							font-size: 14rpx;
							// font-weight: bold;
							// color: #666;
							margin-right: 5rpx;
						}

						.option-text {
							font-size: 14rpx;
							color: #333;
							flex: 1;
						}
					}
				}

				.answer-result {
					height: 50rpx;
					text-align: center;
					padding: 20rpx;
					background-color: #f9f9f9;
					border-radius: 8rpx;

					// .result-icon {
					// 	margin-bottom: 10rpx;
					// }

					.result-text {
						display: block;
						font-size: 16rpx;
						font-weight: bold;
						margin-bottom: 10rpx;

						&.correct {
							color: #4caf50;
						}

						&.incorrect {
							color: #f44336;
						}
					}

					.next-btn {
						display: flex;
						align-items: center;
						justify-content: center;
						height: 30rpx;
						background-color: #2A82E4;
						color: white;
						border: none;
						padding: 10rpx 30rpx;
						border-radius: 6rpx;
						font-size: 14rpx;
					}
				}
			}

			.completion-content {
				text-align: center;
				padding: 40rpx 20rpx;

				.completion-icon {
					margin-bottom: 20rpx;
				}

				.completion-text {
					display: block;
					font-size: 18rpx;
					color: #4caf50;
					font-weight: bold;
					margin-bottom: 20rpx;
				}

				.completion-buttons {
					display: flex;
					flex-direction: column;
					align-items: center;
					justify-content: center;
					gap: 20rpx;
					height: 120rpx;

					.restart-btn {
						display: flex;
						align-items: center;
						justify-content: center;
						background-color: #2A82E4;
						color: white;
						border: none;
						padding: 15rpx 40rpx;
						border-radius: 8rpx;
						font-size: 16rpx;
					}

					.close-btn {
						display: flex;
						align-items: center;
						justify-content: center;
						background-color: #666;
						color: white;
						border: none;
						padding: 15rpx 40rpx;
						border-radius: 8rpx;
						font-size: 16rpx;
					}
				}
			}
		}

		// 模糊层
		.blur-layer {
			position: absolute;
			top: 0;
			right: 0;
			height: 100%;
			background-color: rgba(255, 255, 255, 0.5);
			backdrop-filter: blur(10px);
			transition: width 0.3s ease;
			/* 添加宽度过渡动画 */


			// 右侧滚动按钮
			.button-container {
				position: absolute;
				right: 0;
				z-index: 150;

				.button-item {
					margin: 20rpx;
					background-color: #fff;
					border-radius: 15rpx;
					width: 60rpx;
					height: 60rpx;
					display: flex;
					align-items: center;
					justify-content: center;

					&.active {
						background-color: #2A82E4;
						transform: scale(1.03);
					}
				}
			}

			// 左侧内容
			.content-panel {
				position: fixed;
				width: 300rpx;
				padding: 15rpx 0 0 15rpx;

				// border: red solid 1rpx;
				.physical {
					width: 90%;
					height: 250rpx; // 设置固定高度以启用垂直滚动
					padding: 10rpx 0;

					text {
						font-size: 13rpx;
						margin: 0 0 0 13rpx;
					}
				}

				.block {
					display: flex;
					flex-wrap: wrap;

					/* 允许子元素换行 */
					// border: black solid 1rpx;
					.project-container {
						display: flex;
						justify-content: center;
						align-items: center;
						flex-direction: column;
						/* 垂直排列子元素 */
						margin: 12rpx;
						;
						// border: #2A82E4 solid 1rpx;
						text-align: center;

						.project-button {
							display: flex;
							border: blue solid 1rpx;
							align-items: center;
							justify-content: center;
							background-color: #fff;
							border-radius: 15rpx;
							width: 55rpx;
							height: 55rpx;

							.icon {
								color: #2A82E4;
								size: 30rpx;
							}
						}

						text {
							font-size: 12rpx;
							margin: 5rpx 0 0 0;
						}
					}
				}

				.bodyPosition {
					width: 90%;
					height: 250rpx; // 设置固定高度以启用垂直滚动
					padding: 10rpx 0;

					.project-container {
						display: flex;
						align-items: center;
						margin: 15rpx 0;

						.project-button {
							display: flex;
							align-items: center;
							justify-content: left;
							background-color: #fff;
							border-radius: 15rpx;
							width: 100%;
							height: 60rpx;
							// border: 1rpx solid pink;
							margin-right: 15rpx;

							.icon {
								margin: 0 10rpx 0 10rpx;
								color: #2A82E4;
								size: 30rpx;
							}
						}

						text {
							font-size: 18rpx;
						}
					}
				}

			}
		}
	}

	.temp-quiz-dialog {
		position: fixed;
		z-index: 100;
		top: 0;
		left: 0;
		width: 100vw;
		height: 100vh;
		background: rgba(0, 0, 0, 0.35);
		display: flex;
		align-items: center;
		justify-content: center;

		.temp-quiz-content {
			background: #fff;
			border-radius: 18rpx;
			padding: 20rpx 30rpx 30rpx 30rpx;
			box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.18);
			display: flex;
			flex-direction: column;
			align-items: center;
			min-width: 400rpx;
			max-width: 90vw;

			.temp-quiz-image {
				width: 100%;
				height: 120rpx;
				margin-bottom: 20rpx;
				border-radius: 10rpx;
				object-fit: contain;
				background: #f5f5f5;
			}

			.temp-quiz-question {
				display: flex;
				align-items: center;
				font-size: 18rpx;
				margin-bottom: 20rpx;

				.temp-quiz-input {
					width: 80rpx;
					height: 40rpx;
					margin: 0 10rpx;
					border: 1rpx solid #ccc;
					border-radius: 6rpx;
					font-size: 18rpx;
					text-align: center;
					padding: 0 5rpx;
				}
			}

			.temp-quiz-submit {
				background: #2A82E4;
				color: #fff;
				border: none;
				border-radius: 8rpx;
				padding: 0rpx 40rpx;
				font-size: 16rpx;
				margin-bottom: 5rpx;
			}

			.temp-quiz-result {
				margin: 5rpx 0 0 0;
				font-size: 16rpx;
				font-weight: bold;
			}

			.temp-quiz-result.correct {
				color: #4caf50;
			}

			.temp-quiz-result.incorrect {
				color: #f44336;
			}

			.temp-quiz-close {
				background: #666;
				color: #fff;
				border: none;
				border-radius: 8rpx;
				padding: 0rpx 40rpx;
				font-size: 16rpx;
				margin-top: 5rpx;
			}
		}
	}

	.visual-diagnosis-container {
		position: relative;
		height: 250px;
		width: 330px;
		top: 3%;
		left: 48%;
		z-index: 100;
	}

	.diagnosis-dialog {
		position: absolute;
		max-height: 150rpx;
		width: 180rpx;
		padding: 15rpx;
		background-color: white;
		border-radius: 10rpx;
		box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
	}

	.dialog-top {
		top: 45rpx;
		left: -206rpx;
	}

	.dialog-bottom {
		top: 226rpx;
		left: -3rpx
	}

	.ribs-grid {
		display: flex;
		flex-wrap: wrap;
		gap: 10rpx;
	}

	.rib-item {
		flex: 0 0 calc(50% - 5rpx);
		/* 两列布局 */
		box-sizing: border-box;
	}

	.dialog-content {
		font-size: 14rpx;
		line-height: 1.5;
	}

	.task-table {
		position: fixed;
		left: 13rpx;
		bottom: 5rpx;
		width: 135rpx;
		background-color: #fff;
		border-radius: 10rpx;
		box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 1);
		z-index: 30;
	}

	.table-header,
	.table-row {
		display: flex;
	}

	.header-cell,
	.body-cell {
		flex: 1;
		padding: 12rpx;
		text-align: center;
		font-size: 12rpx;
	}

	.header-cell {
		font-weight: bold;
		background-color: #f0f0f0;
		border-bottom: 1rpx solid #ddd;
	}

	.body-cell {
		border-bottom: 1rpx solid #e0e0e0;
	}

	.completed {
		color: #4CAF50;
	}

	.error {
		color: #F44336;
		font-weight: bold;
	}

	.table-body {
		height: 135rpx;
		overflow-y: auto;
	}

	.rect-box {
		position: absolute;
		background-color: transparent;
		/* 透明背景，只显示边框 */
		box-sizing: border-box;
		z-index: 120;
		/* 确保在最上层 */
	}

	/* 可点击的矩形框 */
	.clickable-rect {
		cursor: pointer;
		/* 显示手型指针 */
	}

	/* 不可点击的矩形框 */
	.no-click-rect {
		pointer-events: none;
		/* 完全禁用所有鼠标事件 */
		cursor: default;
		/* 显示默认指针 */
	}

	.rect-hint {
		position: absolute;
		top: 105%;
		left: -105rpx;
		width: 160rpx;
		transform: translate(-50%, -50%);
		color: black;
		background-color: white;
		padding: 10rpx 20rpx;
		border-radius: 10rpx;
		z-index: 120;
		animation: 5s ease-in 1s finite reverse both running slidein;
	}

	@keyframes fadeInOut {

		0%,
		100% {
			opacity: 0;
		}

		50% {
			opacity: 1;
		}
	}

	.drawing-controls {
		position: absolute;
		top: 48px;
		left: -36%;
		transform: translateX(-50%);
		display: flex;
		flex-direction: column;
		align-items: center;
		z-index: 120;
		padding: 3rpx 5rpx;
		border-radius: 8px;
	}

	/* 画线按钮 */
	.drawing-button {
		padding: 4px 4px;
		border: none;
		border-radius: 4px;
		font-size: 13px;
		color: white;
		background-color: #2A82E4;
		margin: 4px;
		z-index: 120;
	}

	.start-btn {
		position: absolute;
		top: 85px;
		left: -100%;
		transform: translateX(-50%);
	}

	.clear-btn {
		top: -9px;
		left: -216%;
		background-color: #f44336;
	}

	.drawing-canvas {
		position: absolute;
		top: 0;
		left: 0;
		height: 250px;
		width: 330px;
		z-index: 110;
		/* 比矩形框低一层 */
		pointer-events: auto;
	}


	@keyframes fadeIn {
		from {
			opacity: 0;
		}

		to {
			opacity: 1;
		}
	}

	.introduction-popup {
		position: fixed;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 150;
	}

	.introduction-content {
		max-width: 60%;
		max-height: 20%;
		padding: 20rpx;
		top: 0;
		position: absolute;
		background-color: rgba(255, 255, 255, 0.9);
		color: black;
		border-radius: 15rpx;
		box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.15);
		font-size: 14px;
		line-height: 1.5;
		text-align: center;
		animation: popup-appear 0.3s ease;
	}

	@keyframes popup-appear {
		from {
			opacity: 0;
			transform: scale(0.9);
		}

		to {
			opacity: 1;
			transform: scale(1)
		}
	}

	.dynamic-image-container {
		position: absolute;
		top: 0;
		left: 0;
		height: 250px;
		width: 330px;
		z-index: 200;
		/* 提高z-index确保在最上层 */
	}

	.dynamic-image {
		width: 320rpx;
		height: 200rpx;
		object-fit: contain;
	}

	.visual-image {
		position: relative;
		z-index: 100;
	}

	.visual-image-next {
		position: absolute;
		top: -5px;
		left: 0;
		width: 100%;
		height: 100%;
		object-fit: contain;
		z-index: 100;
	}

	.dialog-next {
		top: 50rpx;
		left: -234rpx;
		width: 130rpx;
	}

	.dialog-next1 {
		top: 135rpx;
		left: -234rpx;
		width: 150rpx;
	}

	.chest-expansion-container {
		position: relative;
		/* 改为fixed定位 */
		top: 40%;
		left: 60%;
		transform: translate(-50%, -50%);
		width: 58vw;
		/* 使用视口单位 */
		height: 80vh;
		z-index: 100;
		/* 提高z-index确保在最上层 */
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.dialog-chest {
		top: 154rpx;
		left: -161rpx;
	}

	.chest-expansion-image {
		width: 100%;
		height: 98%;
		object-fit: contain;
		transition: opacity 0.5s ease;
	}

	.chest-expansion-hands {
		position: absolute;
		top: 42%;
		left: 35%;
		width: 121rpx;
		height: 83rpx;
	}

	.chest-expansion {
		position: absolute;
		background-color: white;
		color: black;
		width: 127rpx;
		height: 45rpx;
		top: 28%;
		left: -37%;
		font-size: 16px;
		border-radius: 10rpx;
		z-index: 110;
		padding: 10rpx;
		align-items: center;
		line-height: 1.5;
		box-shadow: 0 4rpx 6rpx rgba(0, 0, 0, 0.3);
	}

	.chest-rect {
		position: absolute;
		background-color: transparent;
		cursor: pointer;
		z-index: 115;
	}

	.view-switch-btn {
		position: absolute;
		bottom: -52px;
		left: 50%;
		transform: translateX(-50%);
		background-color: #2A82E4;
		color: white;
		padding: 5px 15px;
		border-radius: 5px;
		font-size: 14px;
	}

	.chest-click-layer {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		z-index: 105;
		/* 在图片之上，但在矩形框之下 */
		background-color: transparent;
	}

	.target-rect {
		animation: pulse 1.5s infinite;
	}

	@keyframes pulse {
		0% {
			opacity: 0.6;
		}

		50% {
			opacity: 1;
		}

		100% {
			opacity: 0.6;
		}
	}

	/* 添加暖手动画样式 */
	.warm-hands-animation {
		position: absolute;
		top: 30%;
		left: 13%;
		transform: translate(-50%, -50%);
		width: 100px;
		height: 100px;
		z-index: 110;
		animation: warmHandsPulse 1s infinite;
		background-color: white;
	}

	@keyframes warmHandsPulse {
		0% {
			transform: translate(-50%, -50%) scale(1);
		}

		50% {
			transform: translate(-50%, -50%) scale(1.2);
		}

		100% {
			transform: translate(-50%, -50%) scale(1);
		}
	}

	// .clicked {
	// 	background-color: rgba(0, 255, 0, 0.3) !important;
	// }
	.report-prompt {
		position: absolute;
		top: 47%;
		left: -19%;
		transform: translate(-50%, -50%);
		background-color: rgba(255, 255, 255, 0.9);
		padding: 15px 30px;
		border-radius: 10px;
		font-size: 18px;
		font-weight: bold;
		color: #2A82E4;
		z-index: 100;
		box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
	}

	/* 添加样式 */
	.pleura-expansion-container {
		position: relative;
		/* 改为fixed定位 */
		top: 43%;
		left: 64%;
		transform: translate(-50%, -50%);
		width: 50vw;
		/* 使用视口单位 */
		height: 80vh;
		z-index: 100;
		/* 提高z-index确保在最上层 */
		overflow: visible;
	}

	.dialog-pleura {
		top: 118rpx;
		left: -174rpx;
	}

	.pleura-expansion-image {
		position: absolute;
		width: 100%;
		height: 100%;
		object-fit: contain;
		z-index: 100;
	}

	.breathing-animation-image {
		position: absolute;
		width: 100%;
		height: 100%;
		object-fit: contain;
		z-index: 105;
	}


	.movable-area {
		width: 100%;
		height: 100%;
		overflow: visible;
		position: relative;
	}

	.hand-draggable {
		width: 80px;
		height: 80px;
		z-index: 120;
		background-color: transparent;
		border: none;
	}

	.hand-draggable image {
		width: 100%;
		height: 100%;
		opacity: 1;
	}

	.pleura-target-rect {
		position: absolute;
		border: 2px dashed #4CAF50;
		border-radius: 8px;
		z-index: 110;
		background-color: rgba(76, 175, 80, 0.1);
	}

	.pleura-instruction {
		position: absolute;
		bottom: 165px;
		left: -20%;
		transform: translateX(-50%);
		background-color: white;
		padding: 10px 15px;
		border-radius: 10rpx;
		box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
		font-size: 16px;
	}

	/* 添加动画效果 */
	.breathing-animation-image {
		animation: breathe 4s ease-in-out;
	}

	@keyframes breathe {

		0%,
		100% {
			opacity: 0.8;
			transform: scale(1);
		}

		50% {
			opacity: 1;
			transform: scale(1.02);
		}
	}

	.Whole-lung-container {
		position: fixed;
		/* 改为 fixed 确保在视图最上层 */
		top: 0;
		left: 5rpx;
		width: 85%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 100;
		/* 确保在最上层 */
	}

	.percussion-image-container {
		position: absolute;
		top: 49%;
		left: 53%;
		transform: translate(-50%, -50%);
		background-color: rgba(255, 255, 255, 0.9);
		padding: 20rpx;
		border-radius: 10rpx;
		z-index: 101;
		text-align: center;
	}

	.percussion-title {
		font-size: 32rpx;
		font-weight: bold;
		margin-bottom: 20rpx;
		display: block;
	}

	.percussion-images {
		display: flex;
		flex-direction: row;
		gap: 20rpx;
	}

	.percussion-images image {
		width: 177rpx;
		height: 170rpx;
		border: 2rpx solid #ddd;
		border-radius: 8rpx;
	}

	.percussion-images image:active {
		opacity: 0.8;
	}

	.Whole-lung-image {
		width: 49%;
		display: flex;
		flex-direction: column;
		align-items: center;
		position: absolute;
		left: 54%;
	}

	.Whole-lung-image image {
		width: 63%;
		height: 288rpx;
	}

	/* 叩诊点样式 */
	.percussion-rect {
		position: absolute;
		border: 2px solid;
		z-index: 100;
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.percussion-rect:active {
		background-color: rgba(255, 255, 255, 0.7);
	}

	/* 叩诊提示 */
	.percussion-hint {
		position: absolute;
		transform: translateX(-50%);
		background-color: white;
		color: black;
		box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
		padding: 20rpx 15rpx;
		border-radius: 10rpx;
		z-index: 20;
	}

	.hint-left {
		bottom: 110rpx;
		left: -55%;
	}

	.hint-right {
		bottom: 180rpx;
		left: -57%;
	}

	.percussion-id {
		font-size: 12px;
		color: white;
		background-color: rgba(0, 0, 0, 0.5);
		padding: 2px 5px;
		border-radius: 4px;
	}

	/* 音频控制按钮 */
	.audio-controls {
		position: absolute;
		bottom: 75rpx;
		left: 28rpx;
		right: 0;
		display: flex;
		flex-wrap: wrap;
		justify-content: center;
		gap: 10rpx;
		z-index: 20;
		width: 370rpx;
	}

	.audio-controls button {
		padding: 10rpx;
		font-size: 14rpx;
	}

	.percussion-hand {
		z-index: 105;
		pointer-events: none;
		/* 防止图片阻挡点击事件 */
	}

	.pulse-animation {
		animation: percussionPulse 0.4s infinite alternate;
		transform-origin: center bottom;
	}

	@keyframes percussionPulse {
		0% {
			transform: scale(1);
		}

		50% {
			transform: scale(1.1);
		}

		100% {
			transform: scale(1.2);
		}
	}

	.lung-prompt1 {
		font-size: 16px;
		color: black;
		position: absolute;
		left: 0%;
		top: 23%;
		line-height: 1.5;
		font-weight: 600
	}

	.lung-prompt2 {
		font-size: 16px;
		height: 300rpx;
		width: 200rpx;
		color: black;
		position: absolute;
		left: 0%;
		top: 20%;
		line-height: 1.5;
		font-weight: 600;

		.yewo {
			position: absolute;
			top: 1%;
		}

		.four {
			position: absolute;
			top: 10%;
		}

		.six {
			position: absolute;
			top: 27%;
		}

		.eight {
			position: absolute;
			top: 45%;
		}
	}

	.lung-prompt3 {
		font-size: 16px;
		height: 300rpx;
		width: 200rpx;
		color: black;
		position: absolute;
		left: -17%;
		top: 20%;
		line-height: 1.5;
		font-weight: 600;

		.two {
			position: absolute;
			top: 3%;
		}

		.four {
			position: absolute;
			top: 12%;
		}

		.seven {
			position: absolute;
			top: 26%;
		}

		.eight {
			position: absolute;
			top: 33%;
		}

		.ten {
			position: absolute;
			top: 50%;
		}
	}

	.lung-lower-bound-container {
		position: fixed;
		top: 0;
		left: 5rpx;
		width: 85%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 100;
	}

	.lung-lower-bound-image {
		width: 49%;
		display: flex;
		flex-direction: column;
		align-items: center;
		position: absolute;
		left: 54%;
	}

	.lung-lower-bound-image image {
		width: 63%;
		height: 288rpx;
	}

	/* 肺下界特有的提示样式 */
	.lung-prompt1 {
		font-size: 16px;
		color: black;
		position: absolute;
		left: 0%;
		top: 24%;
		line-height: 1.5;
		font-weight: 600;
	}

	.lung-prompt2 {
		font-size: 16px;
		height: 300rpx;
		width: 200rpx;
		color: black;
		position: absolute;
		left: 0%;
		top: 20%;
		font-weight: 600;

		.yewo {
			position: absolute;
			top: 1%;
		}

		.four {
			position: absolute;
			top: 9%;
		}

		.six {
			position: absolute;
			top: 26%;
		}

		.eight {
			position: absolute;
			top: 44%;
		}
	}

	.lung-prompt3 {
		font-size: 16px;
		height: 300rpx;
		width: 200rpx;
		color: black;
		position: absolute;
		left: -17%;
		top: 20%;
		font-weight: 600;

		.two {
			position: absolute;
			top: 3%;
		}

		.seven {
			position: absolute;
			top: 26%;
		}

		.eight {
			position: absolute;
			top: 33%;
		}

		.ten {
			position: absolute;
			top: 48%;
		}
	}

	/* 叩诊点样式 */
	.percussion-rect {
		position: absolute;
		border: 2px solid;
		z-index: 100;
		display: flex;
		justify-content: center;
		align-items: center;
		cursor: pointer;
	}

	.percussion-rect:active {
		background-color: rgba(255, 255, 255, 0.7);
	}

	.percussion-id {
		font-size: 12px;
		color: white;
		background-color: rgba(0, 0, 0, 0.5);
		padding: 2px 5px;
		border-radius: 4px;
	}

	/* 叩诊提示 */
	.percussion-hint {
		position: absolute;
		transform: translateX(-50%);
		background-color: white;
		color: black;
		padding: 20rpx 15rpx;
		border-radius: 15rpx;
		z-index: 20;
		font-size: 16px;
	}

	.hint-left {
		bottom: 110rpx;
		left: -55%;
	}

	.hint-right {
		bottom: 180rpx;
		left: -55%;
	}

	/* 手部动画 */
	.percussion-hand {
		z-index: 105;
		pointer-events: none;
	}

	.pulse-animation {
		animation: percussionPulse 0.4s infinite alternate;
		transform-origin: center bottom;
	}

	@keyframes percussionPulse {
		0% {
			transform: scale(1);
		}

		50% {
			transform: scale(1.1);
		}

		100% {
			transform: scale(1.2);
		}
	}

	.lung-quiz-dialog {
		position: fixed;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 200;
	}

	.lung-quiz-content {
		background-color: white;
		padding: 20rpx;
		border-radius: 15rpx;
		width: 80%;
		max-width: 600rpx;
		box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
	}

	.lung-quiz-title {
		font-size: 18px;
		font-weight: bold;
		text-align: center;
		margin-bottom: 20rpx;
		color: #2A82E4;
	}

	.lung-quiz-question {
		font-size: 16px;
	}

	.quiz-input-group {
		display: flex;
		align-items: center;
		margin: 10rpx 0;
	}

	.lung-quiz-input {
		border: 1px solid #ddd;
		border-radius: 5px;
		padding: 8rpx 15rpx;
		margin: 0 10rpx;
		width: 80rpx;
		text-align: center;
		font-size: 16px;
	}

	.lung-quiz-input:focus {
		border-color: #2A82E4;
		outline: none;
	}

	.lung-quiz-submit {
		background-color: #2A82E4;
		color: white;
		border: none;
		border-radius: 8rpx;
		width: 100%;
		font-size: 16px;
	}

	.tactile-fremitus-container {
		position: relative;
		top: 43%;
		left: 64%;
		transform: translate(-50%, -50%);
		width: 50vw;
		height: 80vh;
		z-index: 100;
	}

	.tactile-image {
		width: 100%;
		height: 100%;
		object-fit: contain;
	}

	.movable-area-tactile {
		width: 100%;
		height: 100%;
		position: absolute;
		top: 0;
		left: 0;
	}

	.hand-draggable-tactile {
		width: 60px;
		height: 60px;
		z-index: 120;
	}

	.hand-draggable-tactile image {
		width: 100%;
		height: 100%;
	}

	.tactile-target-rect {
		position: absolute;
		border: 2px dashed #4CAF50;
		border-radius: 8px;
		z-index: 110;
		background-color: rgba(76, 175, 80, 0.1);
		display: flex;
		justify-content: center;
		align-items: center;
	}

	.position-id {
		font-size: 16px;
		font-weight: bold;
		color: #4CAF50;
	}

	.view-switch-btn-tactile {
		position: absolute;
		bottom: 10px;
		left: 50%;
		transform: translateX(-50%);
		background-color: #2A82E4;
		color: white;
		padding: 5px 15px;
		border-radius: 5px;
		font-size: 14px;
	}

	.tactile-instruction {
		position: absolute;
		bottom: 50px;
		left: 50%;
		transform: translateX(-50%);
		background-color: white;
		padding: 10px 15px;
		border-radius: 10px;
		box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
		font-size: 16px;
	}

	.dialog-tactile {
		top: 118rpx;
		left: -174rpx;
	}

	.lung-mobility-container {
		position: fixed;
		top: 0;
		left: 5rpx;
		width: 85%;
		height: 100%;
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 100;
	}

	.lung-mobility-image {
		width: 50%;
		display: flex;
		flex-direction: column;
		align-items: center;
		position: absolute;
		left: 52%;
		top: -5%;
	}

	.lung-mobility-image image {
		width: 80%;
		height: 288rpx;
	}

	/* 标记点的样式 */
	.marked-rect {
		border-color: green !important;
	}

	.center-dot {
		width: 12px;
		height: 12px;
		background-color: #ff5722;
		border-radius: 50%;
		position: absolute;
		top: 50%;
		left: 50%;
		transform: translate(-50%, -50%);
		box-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
	}

	.marking-mode {
		cursor: pointer;
		animation: pulse-marking 1s infinite;
	}

	@keyframes pulse-marking {
		0% {
			transform: scale(1);
		}

		50% {
			transform: scale(1.1);
		}

		100% {
			transform: scale(1);
		}
	}

	/* 叩诊提示 */
	.hint-mobility1 {
		bottom: 180rpx;
		left: 22%;
	}

	.hint-mobility2 {
		bottom: 120rpx;
		left: 8%;
	}

	/* 优化后的小尺子样式 */
	.ruler-container {
		position: absolute;
		z-index: 102;
		display: flex;
		align-items: center;
	}

	.ruler-line {
		width: 3px;
		background: linear-gradient(to bottom, #3498db, #2c3e50);
		border-radius: 2px;
		box-shadow: 0 2px 4px rgba(52, 152, 219, 0.4);
	}

	.ruler {
		background: white;
		border-radius: 6px;
		padding: 5px 8px;
		margin-left: 42px;
		box-shadow: 0 3px 10px rgba(0, 0, 0, 0.15);
		border: 1px solid #e0e0e0;
		min-width: 40px;
		text-align: center;
		font-weight: bold;
		color: #2c3e50;
		font-size: 15px;
	}

	.ruler-mark {
		position: absolute;
		width: 12px;
		height: 2px;
		background-color: #34495e;
		left: -5px;
	}

	.ruler-mark-label {
		position: absolute;
		font-size: 15px;
		color: #000000;
		left: 14px;
		transform: translateY(-50%);
	}

	/* 距离结果样式 */
	.distance-result {
		position: absolute;
		padding: 5px 10px;
		background-color: #2A82E4;
		color: white;
		font-size: 14px;
		border-radius: 4px;
		z-index: 103;
	}
</style>